বাইন্ডারের থ্রেডিং মডেলটি এমনভাবে ডিজাইন করা হয়েছে যাতে লোকাল ফাংশন কলগুলো সহজতর হয়, যদিও সেই কলগুলো কোনো রিমোট প্রসেসের উদ্দেশ্যে করা হয়ে থাকে। বিশেষত, কোনো নোড হোস্টকারী যেকোনো প্রসেসের অবশ্যই এক বা একাধিক বাইন্ডার থ্রেডের একটি পুল থাকতে হবে, যা সেই প্রসেসে হোস্ট করা নোডগুলোর ট্রানজ্যাকশন পরিচালনা করবে।
সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস লেনদেন
বাইন্ডার সিনক্রোনাস এবং অ্যাসিনক্রোনাস ট্রানজ্যাকশন সমর্থন করে। নিম্নলিখিত বিভাগগুলিতে প্রতিটি ট্রানজ্যাকশন প্রকার কীভাবে সম্পাদিত হয় তা ব্যাখ্যা করা হয়েছে।
সিঙ্ক্রোনাস লেনদেন
সিঙ্ক্রোনাস ট্রানজ্যাকশনগুলো নোডে সম্পাদিত না হওয়া পর্যন্ত এবং কলার সেই ট্রানজ্যাকশনের উত্তর না পাওয়া পর্যন্ত ব্লক হয়ে থাকে। নিচের চিত্রে দেখানো হয়েছে কীভাবে একটি সিঙ্ক্রোনাস ট্রানজ্যাকশন সম্পাদিত হয়:

চিত্র ১. যুগপৎ লেনদেন।
একটি সিনক্রোনাস ট্রানজ্যাকশন সম্পাদন করতে, বাইন্ডার নিম্নলিখিত কাজগুলো করে থাকে:
- টার্গেট থ্রেডপুলের থ্রেডগুলো (T2 এবং T3) আগত কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে আহ্বান জানায়।
- কার্নেল একটি নতুন ট্রানজ্যাকশন গ্রহণ করে এবং ট্রানজ্যাকশনটি পরিচালনা করার জন্য টার্গেট প্রসেসে একটি থ্রেড (T2) জাগিয়ে তোলে।
- কলিং থ্রেড (T1) ব্লক হয়ে উত্তরের জন্য অপেক্ষা করে।
- টার্গেট প্রসেসটি ট্রানজ্যাকশনটি সম্পাদন করে এবং একটি প্রত্যুত্তর ফেরত দেয়।
- টার্গেট প্রসেসের (T2) থ্রেডটি নতুন কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে পুনরায় কল করে।
অ্যাসিঙ্ক্রোনাস লেনদেন
অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন সম্পূর্ণ হওয়ার জন্য ব্লক হয় না; ট্রানজ্যাকশনটি কার্নেলে পাঠানো হলেই কলিং থ্রেডটি আনব্লক হয়ে যায়। নিচের চিত্রে দেখানো হয়েছে কীভাবে একটি অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন সম্পাদিত হয়:

চিত্র ২. অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশন।
- টার্গেট থ্রেডপুলের থ্রেডগুলো (T2 এবং T3) আগত কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে আহ্বান জানায়।
- কার্নেল একটি নতুন ট্রানজ্যাকশন গ্রহণ করে এবং ট্রানজ্যাকশনটি পরিচালনা করার জন্য টার্গেট প্রসেসে একটি থ্রেড (T2) জাগিয়ে তোলে।
- কলিং থ্রেড (T1) তার কার্য সম্পাদন অব্যাহত রাখে।
- টার্গেট প্রসেসটি ট্রানজ্যাকশনটি সম্পাদন করে এবং একটি প্রত্যুত্তর ফেরত দেয়।
- টার্গেট প্রসেসের (T2) থ্রেডটি নতুন কাজের জন্য অপেক্ষা করতে কার্নেল ড্রাইভারকে পুনরায় কল করে।
একটি সিঙ্ক্রোনাস বা অ্যাসিঙ্ক্রোনাস ফাংশন শনাক্ত করুন
AIDL ফাইলে oneway হিসেবে চিহ্নিত ফাংশনগুলো অ্যাসিঙ্ক্রোনাস। উদাহরণস্বরূপ:
oneway void someCall();
যদি কোনো ফাংশনকে oneway হিসেবে চিহ্নিত করা না হয়, তাহলে সেটি একটি সিনক্রোনাস ফাংশন, এমনকি যদি ফাংশনটি void রিটার্ন করে তবুও।
অ্যাসিঙ্ক্রোনাস লেনদেনের ক্রমিকীকরণ
বাইন্ডার যেকোনো একটি নোড থেকে অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনগুলোকে সিরিয়ালাইজ করে। নিচের চিত্রে দেখানো হয়েছে বাইন্ডার কীভাবে অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনগুলোকে সিরিয়ালাইজ করে:

চিত্র ৩. অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের ক্রমিকীকরণ।
- টার্গেট থ্রেডপুলের (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()চালায়, যা A একই থ্রেড A1-এ চালায়।
নিম্নলিখিত চিত্রটি থ্রেড এক্সিকিউশন দেখায় যদি bar() ইমপ্লিমেন্টকারী নোডটি একটি ভিন্ন প্রসেসে থাকে:

চিত্র ৫. বিভিন্ন প্রসেসে অবস্থিত নেস্টেড ট্রানজ্যাকশন।
- থ্রেড A1 `
foo()চালানোর অনুরোধ করেছে। - এই অনুরোধের অংশ হিসেবে, থ্রেড B1 `
bar()চালায়, যা অন্য একটি থ্রেড C1-এ চলে।
নিম্নলিখিত চিত্রটি দেখায় কিভাবে থ্রেডটি ট্রানজ্যাকশন চেইনের যেকোনো স্থানে একই প্রসেস পুনরায় ব্যবহার করে:

চিত্র ৬. একটি থ্রেড পুনঃব্যবহারকারী নেস্টেড ট্রানজ্যাকশন।
- প্রসেস A, প্রসেস B-কে আহ্বান করে।
- প্রসেস B, প্রসেস C-কে আহ্বান করে।
- এরপর প্রসেস C, প্রসেস A-কে পুনরায় কল করে এবং কার্নেল প্রসেস A-তে ট্রানজ্যাকশন চেইনের অংশ হিসেবে A1 থ্রেডটিকে পুনরায় ব্যবহার করে।
অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের ক্ষেত্রে নেস্টিং-এর কোনো ভূমিকা নেই; ক্লায়েন্ট অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের ফলাফলের জন্য অপেক্ষা করে না, তাই এখানে কোনো নেস্টিং নেই। যদি কোনো অ্যাসিঙ্ক্রোনাস ট্রানজ্যাকশনের হ্যান্ডলার সেই প্রসেসে কল করে যা ওই ট্রানজ্যাকশনটি জারি করেছিল, তবে সেই ট্রানজ্যাকশনটি ওই প্রসেসের যেকোনো খালি থ্রেডে হ্যান্ডেল করা যেতে পারে।
ডেডলক এড়িয়ে চলুন
নিচের ছবিটি একটি সাধারণ ডেডলক দেখাচ্ছে:

চিত্র ৭. সাধারণ অচলাবস্থা।
- প্রসেস A মিউটেক্স MA গ্রহণ করে এবং প্রসেস B-কে একটি বাইন্ডার কল (T1) পাঠায়, যেটিও মিউটেক্স MB গ্রহণ করার চেষ্টা করে।
- একই সাথে, প্রসেস B মিউটেক্স MB গ্রহণ করে এবং প্রসেস A-কে একটি বাইন্ডার কল (T2) পাঠায়, যেটি মিউটেক্স MA গ্রহণ করার চেষ্টা করে।
যদি এই ট্রানজ্যাকশনগুলো একে অপরের সাথে ওভারল্যাপ করে, তাহলে অন্য প্রসেসটির মিউটেক্স ছেড়ে দেওয়ার জন্য অপেক্ষা করার সময় প্রতিটি ট্রানজ্যাকশন তার নিজের প্রসেসে একটি মিউটেক্স দখল করে নিতে পারে, যার ফলে ডেডলক তৈরি হতে পারে।
বাইন্ডার ব্যবহার করার সময় ডেডলক এড়াতে, বাইন্ডার কল করার সময় কোনো লক ধরে রাখবেন না।
লক অর্ডারিং নিয়ম এবং ডেডলক
একটি একক এক্সিকিউশন এনভায়রনমেন্টের মধ্যে, একটি লক অর্ডারিং রুল ব্যবহার করে প্রায়শই ডেডলক এড়ানো হয়। তবে, প্রসেসগুলোর মধ্যে এবং কোডবেসগুলোর মধ্যে কল করার সময়, বিশেষ করে যখন কোড আপডেট করা হয়, তখন একটি অর্ডারিং রুল বজায় রাখা এবং সমন্বয় করা অসম্ভব হয়ে পড়ে।
একক মিউটেক্স এবং ডেডলক
নেস্টেড ট্রানজ্যাকশনের ক্ষেত্রে, প্রসেস B একটি মিউটেক্স ধারণকারী প্রসেস A-এর একই থ্রেডে সরাসরি কলব্যাক করতে পারে। সুতরাং, অপ্রত্যাশিত রিকারশনের কারণে, একটিমাত্র মিউটেক্স দিয়েও ডেডলক হওয়ার সম্ভাবনা থাকে।
অ্যাসিঙ্ক্রোনাস কল এবং ডেডলক
যদিও অ্যাসিঙ্ক্রোনাস বাইন্ডার কলগুলো সম্পূর্ণ হওয়ার জন্য ব্লক করে না, তবুও অ্যাসিঙ্ক্রোনাস কলের জন্য লক ধরে রাখা এড়িয়ে চলা উচিত। যদি আপনি লক ধরে রাখেন, তাহলে কোনো একমুখী কল ভুলবশত সিঙ্ক্রোনাস কলে পরিবর্তিত হয়ে গেলে আপনি লকিং সংক্রান্ত সমস্যার সম্মুখীন হতে পারেন।
একক বাইন্ডার থ্রেড এবং ডেডলক
বাইন্ডারের ট্রানজ্যাকশন মডেলে রি-এন্ট্রান্সির সুবিধা রয়েছে, তাই কোনো প্রসেসে একটিমাত্র বাইন্ডার থ্রেড থাকলেও লকিংয়ের প্রয়োজন হয়। উদাহরণস্বরূপ, ধরুন আপনি একটি সিঙ্গেল-থ্রেডেড প্রসেস A-তে একটি লিস্টের ওপর ইটারেট করছেন। লিস্টের প্রতিটি আইটেমের জন্য আপনি একটি আউটগোয়িং বাইন্ডার ট্রানজ্যাকশন সম্পন্ন করেন। আপনি যে ফাংশনটি কল করছেন তার ইমপ্লিমেন্টেশন যদি প্রসেস A-তে হোস্ট করা কোনো নোডে একটি নতুন বাইন্ডার ট্রানজ্যাকশন তৈরি করে, তবে সেই ট্রানজ্যাকশনটি সেই একই থ্রেডে হ্যান্ডেল করা হবে যেটি লিস্টটির ওপর ইটারেট করছিল। যদি সেই ট্রানজ্যাকশনের ইমপ্লিমেন্টেশন একই লিস্টটিকে মডিফাই করে, তাহলে পরবর্তীতে লিস্টটির ওপর ইটারেট করা চালিয়ে গেলে আপনি সমস্যার সম্মুখীন হতে পারেন।
থ্রেডপুলের আকার কনফিগার করুন
যখন কোনো সার্ভিসের একাধিক ক্লায়েন্ট থাকে, তখন থ্রেডপুলে আরও থ্রেড যোগ করলে তা কনটেনশন কমাতে পারে এবং সমান্তরালভাবে আরও বেশি কল পরিচালনা করতে পারে। কনকারেন্সি সঠিকভাবে সামলানোর পর আপনি আরও থ্রেড যোগ করতে পারেন। আরও থ্রেড যোগ করার ফলে একটি সমস্যা হতে পারে যে, কম চাপের কাজের সময় কিছু থ্রেড অব্যবহৃত থেকে যেতে পারে।
কনফিগার করা সর্বোচ্চ সংখ্যা পর্যন্ত চাহিদা অনুযায়ী থ্রেড তৈরি করা হয়। একটি বাইন্ডার থ্রেড তৈরি হয়ে গেলে, সেটিকে হোস্টকারী প্রসেসটি শেষ না হওয়া পর্যন্ত সেটি সচল থাকে।
libbinder লাইব্রেরিতে ডিফল্টভাবে ১৫টি থ্রেড থাকে। এই মান পরিবর্তন করতে setThreadPoolMaxThreadCount ব্যবহার করুন:
using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);