Binder-এর থ্রেডিং মডেলটি লোকাল ফাংশন কলকে সহজ করার জন্য ডিজাইন করা হয়েছে, এমনকি সেইসব কল কোনও রিমোট প্রসেসে করা হতে পারে। নির্দিষ্টভাবে, কোনও নোড হোস্ট করা যেকোনও প্রসেসে সেই প্রসেসে হোস্ট করা নোডে ট্রানজ্যাকশন ম্যানেজ করার জন্য এক বা একাধিক বাইন্ডার থ্রেডের পুল থাকতে হবে।
সিনক্রোনাস ও অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন
বাইন্ডার সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন সাপোর্ট করে। নিম্নলিখিত বিভাগ থেকে প্রতিটি ট্রানজ্যাকশনের ধরন কীভাবে কার্যকর করা হয় তা ব্যাখ্যা করা হয়েছে।
সিনক্রোনাস ট্রানজ্যাকশন
সিঙ্ক্রোনাস ট্রানজ্যাকশন নোডে এক্সিকিউট না হওয়া পর্যন্ত এবং সেই ট্রানজ্যাকশনের উত্তর কলার না পাওয়া পর্যন্ত ব্লক করে থাকে। নিম্নলিখিত ছবিতে দেখানো হয়েছে যে কীভাবে সিঙ্ক্রোনাস ট্রানজ্যাকশন এক্সিকিউট করা হয়:
ছবি ১. সিঙ্ক্রোনাস ট্রানজ্যাকশন।
সিঙ্ক্রোনাস ট্রানজ্যাকশন এক্সিকিউট করতে, বাইন্ডার নিম্নলিখিত কাজগুলি করে:
- টার্গেট থ্রেডপুলে (T2 ও T3) থ্রেডগুলি ইনকামিং কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে কল করে।
- কার্নেল একটি নতুন ট্রানজ্যাকশন পায় এবং ট্রানজ্যাকশন ম্যানেজ করার জন্য টার্গেট প্রসেসে একটি থ্রেড (T2) জাগিয়ে তোলে।
- কলিং থ্রেড (T1) ব্লক করে এবং উত্তরের জন্য অপেক্ষা করে।
- টার্গেট প্রসেস ট্রানজ্যাকশনটি এক্সিকিউট করে এবং একটি উত্তর ফেরত পাঠায়।
- টার্গেট প্রসেসের (T2) থ্রেডটি নতুন কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে কল ব্যাক করে।
অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন
অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন সম্পূর্ণ হওয়ার জন্য ব্লক করে না; ট্রানজ্যাকশনটি কার্নেলে পাস করা হলে কলিং থ্রেড আনব্লক হয়ে যায়। নিচের ছবিতে দেখানো হয়েছে যে কীভাবে একটি অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন এক্সিকিউট করা হয়:
ছবি ২. অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন।
- টার্গেট থ্রেডপুলে (T2 ও T3) থ্রেডগুলি ইনকামিং কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে কল করে।
- কার্নেল একটি নতুন ট্রানজ্যাকশন পায় এবং ট্রানজ্যাকশন ম্যানেজ করার জন্য টার্গেট প্রসেসে একটি থ্রেড (T2) জাগিয়ে তোলে।
- কলিং থ্রেড (T1) এক্সিকিউশন চালিয়ে যায়।
- টার্গেট প্রসেস ট্রানজ্যাকশনটি এক্সিকিউট করে এবং একটি উত্তর ফেরত পাঠায়।
- টার্গেট প্রসেসের (T2) থ্রেড কার্নেল ড্রাইভারকে কল ব্যাক করে নতুন কাজের জন্য অপেক্ষা করতে বলে।
সিনক্রোনাস বা অ্যাসিঙ্ক্রোনাস ফাংশন শনাক্ত করা
AIDL ফাইলে oneway হিসেবে চিহ্নিত ফাংশনগুলি অ্যাসিঙ্ক্রোনাস। যেমন:
oneway void someCall();
কোনও ফাংশন oneway হিসেবে চিহ্নিত করা না থাকলে, সেটি একটি সিঙ্ক্রোনাস ফাংশন, এমনকি
ফাংশনটি void রিটার্ন করলেও।
অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের সিরিয়ালাইজেশন
Binder যেকোনও একটি নোড থেকে অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন সিরিয়ালাইজ করে। নিম্নলিখিত ছবিতে দেখানো হয়েছে যে কীভাবে বাইন্ডার অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন সিরিয়ালাইজ করে:
ছবি ৩. অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের সিরিয়ালাইজেশন।
- টার্গেট থ্রেডপুলে (B1 এবং B2) থ্রেডগুলি ইনকামিং কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে কল করে।
- একই নোডে (N1) দুটি ট্রানজ্যাকশন (T1 ও T2) কার্নেলে পাঠানো হয়।
- কার্নেল একটি নতুন ট্রানজ্যাকশন পায় এবং যেহেতু এটি একই নোড (N1) থেকে এসেছে, তাই এটি সেগুলিকে সিরিয়ালাইজ করে।
- অন্য নোডে (N2) আরেকটি ট্রানজ্যাকশন কার্নেলে পাঠানো হয়।
- কার্নেল তৃতীয় ট্রানজ্যাকশনটি পায় এবং ট্রানজ্যাকশন ম্যানেজ করার জন্য টার্গেট প্রসেসে একটি থ্রেড (B2) জাগিয়ে তোলে।
- টার্গেট প্রসেস প্রতিটি ট্রানজ্যাকশন এক্সিকিউট করে এবং উত্তর দেয়।
নেস্টেড ট্রানজ্যাকশন
সিঙ্ক্রোনাস ট্রানজ্যাকশন নেস্ট করা যেতে পারে; একটি থ্রেড যা একটি ট্রানজ্যাকশন পরিচালনা করছে সেটি একটি নতুন ট্রানজ্যাকশন ইস্যু করতে পারে। নেস্টেড ট্রানজ্যাকশনটি আলাদা প্রসেস বা একই প্রসেস থেকে হতে পারে যেখান থেকে আপনি বর্তমান ট্রানজ্যাকশনটি পেয়েছেন। এই আচরণ লোকাল ফাংশন কলের মতো। যেমন, ধরে নিন আপনার কাছে নেস্টেড ফাংশন সহ একটি ফাংশন আছে:
def outer_function(x):
def inner_function(y):
def inner_inner_function(z):
এগুলি লোকাল কল হলে, একই থ্রেডে এক্সিকিউট করা হয়।
বিশেষত, যদি inner_function-এর কলারও inner_inner_function প্রয়োগকারী নোডকে হোস্ট করা
প্রসেস হয়, তাহলে inner_inner_function-এ কল
একই থ্রেডে এক্সিকিউট করা হয়।
নিচের ছবিতে দেখানো হয়েছে যে কীভাবে বাইন্ডার নেস্টেড ট্রানজ্যাকশন ম্যানেজ করে:
ছবি ৪. নেস্টেড ট্রানজ্যাকশন।
- থ্রেড A1 অনুরোধ চলছে
foo()। - এই অনুরোধের অংশ হিসেবে, থ্রেড B1
bar()রান করে যা একই থ্রেড A1-এ রান করে।
যে নোড
bar() প্রয়োগ করে সেটি অন্য প্রসেসে থাকলে, নিম্নলিখিত ছবিতে থ্রেড এক্সিকিউশন দেখানো হয়েছে:
ছবি ৫. বিভিন্ন প্রসেসে নেস্টেড ট্রানজ্যাকশন।
- থ্রেড A1 অনুরোধ চলছে
foo()। - এই অনুরোধের অংশ হিসেবে, থ্রেড B1
bar()রান করে যা অন্য থ্রেড C1-এ রান করে।
নিচের ছবিতে দেখানো হয়েছে যে কীভাবে থ্রেড ট্রানজ্যাকশন চেনের যেকোনও জায়গায় একই প্রসেস আবার ব্যবহার করে:
ছবি ৬. নেস্টেড ট্রানজ্যাকশন থ্রেড আবার ব্যবহার করছে।
- প্রসেস A থেকে প্রসেস B-তে কল করা।
- প্রসেস B, প্রসেস C-কে কল করে।
- প্রসেস C তারপরে প্রসেস A-তে কল করে এবং কার্নেল প্রসেস A-তে থ্রেড A1 আবার ব্যবহার করে যা ট্রানজ্যাকশন চেনের অংশ।
অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের ক্ষেত্রে, নেস্টিং কোনও ভূমিকা পালন করে না; ক্লায়েন্ট অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের ফলাফলের জন্য অপেক্ষা করে না, তাই কোনও নেস্টিং নেই। অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের হ্যান্ডলার যদি সেই অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন ইস্যু করা প্রসেসে কল করে, তাহলে সেই ট্রানজ্যাকশনটি সেই প্রসেসের যেকোনও ফ্রি থ্রেডে হ্যান্ডেল করা যেতে পারে।
ডেডলক এড়িয়ে চলুন
নিচের ছবিতে একটি সাধারণ ডেডলক দেখানো হয়েছে:
ছবি ৭. সাধারণ ডেডলক।
- প্রসেস A, মিউটেক্স MA নেয় এবং প্রসেস B-কে একটি বাইন্ডার কল (T1) করে, যা মিউটেক্স MB নেওয়ার চেষ্টা করে।
- একই সাথে, প্রসেস B মিউটেক্স MB নেয় এবং প্রসেস A-কে বাইন্ডার কল (T2) করে। প্রসেস A মিউটেক্স MA নেওয়ার চেষ্টা করে।
এইসব ট্রানজ্যাকশন ওভারল্যাপ করলে, প্রতিটি ট্রানজ্যাকশন তার প্রসেসে একটি মিউটেক্স নিতে পারে, অন্য প্রসেসটি মিউটেক্স রিলিজ করার জন্য অপেক্ষা করার সময়, যার ফলে ডেডলক হতে পারে।
বাইন্ডার ব্যবহার করার সময় ডেডলক এড়াতে, বাইন্ডার কল করার সময় কোনও লক হোল্ড করবেন না।
লক অর্ডারিং নিয়ম ও ডেডলক
সিঙ্গেল এক্সিকিউশন এনভায়রনমেন্টের মধ্যে, লক অর্ডারিং নিয়মের মাধ্যমে প্রায়ই ডেডলক এড়ানো যায়। তবে, প্রসেস ও কোডবেসের মধ্যে কল করার সময়, বিশেষ করে কোড আপডেট করা হলে, অর্ডার করার নিয়ম বজায় রাখা ও সমন্বয় করা অসম্ভব হয়ে পড়ে।
সিঙ্গেল মিউটেক্স ও ডেডলক
নেস্টেড ট্রানজ্যাকশনের মাধ্যমে, প্রসেস B সরাসরি প্রসেস A-এর একই থ্রেডে কল করতে পারে, যা একটি মিউটেক্স হোল্ড করে। তাই, অপ্রত্যাশিত রিক্রেশনের কারণে, একটি মিউটেক্সের সাথে এখনও ডেডলক পাওয়া সম্ভব।
অ্যাসিঙ্ক্রোনাস কল ও ডেডলক
অ্যাসিঙ্ক্রোনাস বাইন্ডার কল সম্পূর্ণ হওয়ার জন্য ব্লক না করলেও, অ্যাসিঙ্ক্রোনাস কলের জন্য লক হোল্ড করা এড়িয়ে চলুন। আপনি লক হোল্ড করলে, একমুখী কল ভুলবশত সিঙ্ক্রোনাস কলে পরিবর্তিত হলে, আপনি লকিং সংক্রান্ত সমস্যার সম্মুখীন হতে পারেন।
সিঙ্গেল বাইন্ডার থ্রেড ও ডেডলক
বাইন্ডারের ট্রানজ্যাকশন মডেল রি-এন্ট্রি করার অনুমতি দেয়, তাই কোনও প্রসেসে একটি বাইন্ডার থ্রেড থাকলেও, আপনাকে এখনও লকিং করতে হবে। যেমন, ধরুন আপনি সিঙ্গেল-থ্রেডেড প্রসেস A-তে একটি তালিকা নিয়ে ইটারেট করছেন। তালিকার প্রতিটি আইটেমের জন্য, আপনি একটি আউটগোয়িং বাইন্ডার ট্রানজ্যাকশন করেন। আপনি যে ফাংশন কল করছেন সেটি যদি প্রসেস A-তে হোস্ট করা নোডে নতুন বাইন্ডার ট্রানজ্যাকশন তৈরি করে, তাহলে সেই ট্রানজ্যাকশনটি একই থ্রেডে হ্যান্ডেল করা হয় যেটি তালিকাটি পুনরাবৃত্তি করছিল। সেই ট্রানজ্যাকশন প্রয়োগ করার ফলে একই তালিকা পরিবর্তিত হলে, পরে সেই তালিকা নিয়ে কাজ করার সময় আপনি সমস্যার সম্মুখীন হতে পারেন।
থ্রেডপুল সাইজ কনফিগার করা
কোনও পরিষেবার একাধিক ক্লায়েন্ট থাকলে, থ্রেডপুলে আরও থ্রেড যোগ করলে প্রতিযোগিতা কমে যায় এবং সমান্তরালভাবে আরও বেশি কল পরিবেশন করা যায়। আপনি কনকারেন্সি সঠিকভাবে ম্যানেজ করার পরে, আরও থ্রেড যোগ করতে পারবেন। এমন একটি সমস্যা যা আরও থ্রেড যোগ করার কারণে হতে পারে, যার ফলে কিছু থ্রেড শান্ত ওয়ার্কলোডের সময় ব্যবহার নাও করা হতে পারে।
কনফিগার করা সর্বাধিক সংখ্যায় না পৌঁছানো পর্যন্ত চাহিদা অনুযায়ী থ্রেড তৈরি করা হয়। একটি বাইন্ডার থ্রেড তৈরি করার পরে, এটি যে প্রসেস হোস্ট করছে সেটি শেষ না হওয়া পর্যন্ত এটি চালু থাকে।
libbinder লাইব্রেরিতে ডিফল্ট হিসেবে ১৫টি থ্রেড থাকে। এই ভ্যালু পরিবর্তন করতে
setThreadPoolMaxThreadCount ব্যবহার করুন:
using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);