ফাস্ট মেসেজ কিউ (FMQ)

HIDL এর রিমোট প্রসিডিওর কল (RPC) অবকাঠামো বাইন্ডার মেকানিজম ব্যবহার করে, যার অর্থ কলগুলি ওভারহেড জড়িত, কার্নেল অপারেশনের প্রয়োজন, এবং শিডিউলার অ্যাকশন ট্রিগার করতে পারে। যাইহোক, যেসব ক্ষেত্রে কম ওভারহেড এবং কোনো কার্নেল জড়িত না থাকা প্রসেসের মধ্যে ডেটা স্থানান্তর করতে হবে, ফাস্ট মেসেজ কিউ (FMQ) সিস্টেম ব্যবহার করা হয়।

FMQ পছন্দসই বৈশিষ্ট্য সহ বার্তা সারি তৈরি করে। আপনি একটি HIDL RPC কলের মাধ্যমে একটি MQDescriptorSync বা MQDescriptorUnsync অবজেক্ট পাঠাতে পারেন এবং বার্তা সারি অ্যাক্সেস করতে প্রাপ্তি প্রক্রিয়া দ্বারা অবজেক্টটি ব্যবহার করা হয়।

সারির ধরন

অ্যান্ড্রয়েড দুটি সারি ধরনের সমর্থন করে ( স্বাদ নামে পরিচিত):

  • আনসিঙ্ক্রোনাইজড সারিগুলিকে ওভারফ্লো করার অনুমতি দেওয়া হয় এবং অনেক পাঠক থাকতে পারে; প্রতিটি পাঠকের অবশ্যই সময়মতো ডেটা পড়তে হবে বা এটি হারাতে হবে।
  • সিঙ্ক্রোনাইজড সারিগুলিকে ওভারফ্লো করার অনুমতি দেওয়া হয় না এবং শুধুমাত্র একজন পাঠক থাকতে পারে৷

উভয় সারির প্রকারকে আন্ডারফ্লো করার অনুমতি দেওয়া হয় না (একটি খালি সারি থেকে পড়া ব্যর্থ হয়) এবং শুধুমাত্র একজন লেখক থাকতে পারে।

আনসিঙ্ক্রোনাইজড সারি

একটি অসিঙ্ক্রোনাইজড সারিতে শুধুমাত্র একজন লেখক আছে, কিন্তু পাঠকদের সংখ্যা যেকোনও থাকতে পারে। সারির জন্য একটি লেখার অবস্থান আছে; যাইহোক, প্রতিটি পাঠক তার নিজস্ব স্বাধীন পঠন অবস্থানের উপর নজর রাখে।

সারিতে লেখা সবসময় সফল হয় (ওভারফ্লো পরীক্ষা করা হয় না) যতক্ষণ না তারা কনফিগার করা সারির ক্ষমতার চেয়ে বড় না হয় (সারি ধারণক্ষমতার চেয়ে বড় লেখা অবিলম্বে ব্যর্থ হয়)। যেহেতু প্রতিটি পাঠকের একটি আলাদা পড়ার অবস্থান থাকতে পারে, প্রতিটি পাঠকের প্রতিটি ডেটা পড়ার জন্য অপেক্ষা করার পরিবর্তে, যখনই নতুন লেখার জন্য স্থানের প্রয়োজন হয় তখন ডেটা সারি থেকে পড়ে যায়।

সারির শেষ থেকে পড়ে যাওয়ার আগে ডেটা পুনরুদ্ধার করার জন্য পাঠকদের দায়বদ্ধ। একটি রিড যা উপলব্ধ থেকে বেশি ডেটা পড়ার চেষ্টা করে তা হয় অবিলম্বে ব্যর্থ হয় (যদি অবরোধ না করা হয়) বা পর্যাপ্ত ডেটা উপলব্ধ হওয়ার জন্য অপেক্ষা করে (যদি ব্লক করা হয়)। একটি পঠন যা সারির ক্ষমতার চেয়ে বেশি ডেটা পড়ার চেষ্টা করে তা অবিলম্বে ব্যর্থ হয়।

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

সিঙ্ক্রোনাইজড সারি

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

একটি FMQ সেট আপ করুন

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

প্রথম MessageQueue অবজেক্ট তৈরি করুন

একটি একক কলের সাথে একটি বার্তা সারি তৈরি এবং কনফিগার করা হয়েছে:

#include <fmq/MessageQueue.h>
using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
using android::hardware::MQDescriptorSync;
using android::hardware::MQDescriptorUnsync;
using android::hardware::MessageQueue;
....
// For a synchronized nonblocking FMQ
mFmqSynchronized =
  new (std::nothrow) MessageQueue<uint16_t, kSynchronizedReadWrite>
      (kNumElementsInQueue);
// For an unsynchronized FMQ that supports blocking
mFmqUnsynchronizedBlocking =
  new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
      (kNumElementsInQueue, true /* enable blocking operations */);
  • MessageQueue<T, flavor>(numElements) ইনিশিয়ালাইজার একটি বস্তু তৈরি করে এবং শুরু করে যা বার্তা সারি কার্যকারিতা সমর্থন করে।
  • MessageQueue<T, flavor>(numElements, configureEventFlagWord) ইনিশিয়ালাইজার একটি অবজেক্ট তৈরি করে এবং শুরু করে যা ব্লক করার সাথে বার্তা সারি কার্যকারিতা সমর্থন করে।
  • flavor হয় একটি সিঙ্ক্রোনাইজড সারির জন্য kSynchronizedReadWrite অথবা একটি unsynchronized সারির জন্য kUnsynchronizedWrite হতে পারে।
  • uint16_t (এই উদাহরণে) যেকোন HIDL-সংজ্ঞায়িত প্রকার হতে পারে যাতে নেস্টেড বাফার (কোনও string বা vec প্রকার), হ্যান্ডেল বা ইন্টারফেস জড়িত নয়।
  • kNumElementsInQueue এন্ট্রির সংখ্যায় সারির আকার নির্দেশ করে; এটি ভাগ করা মেমরি বাফারের আকার নির্ধারণ করে যা সারির জন্য বরাদ্দ করা হয়।

দ্বিতীয় MessageQueue অবজেক্ট তৈরি করুন

বার্তা সারির দ্বিতীয় দিকটি প্রথম দিক থেকে প্রাপ্ত একটি MQDescriptor অবজেক্ট ব্যবহার করে তৈরি করা হয়েছে। MQDescriptor অবজেক্ট একটি HIDL বা AIDL RPC কলের মাধ্যমে পাঠানো হয় সেই প্রক্রিয়ায় যা বার্তা সারির দ্বিতীয় প্রান্তে থাকে। MQDescriptor এ সারি সম্পর্কে তথ্য রয়েছে, যার মধ্যে রয়েছে:

  • বাফার ম্যাপ এবং পয়েন্টার লিখতে তথ্য.
  • পঠিত পয়েন্টার ম্যাপ করার জন্য তথ্য (যদি সারিটি সিঙ্ক্রোনাইজ করা হয়)।
  • ইভেন্ট পতাকা শব্দ ম্যাপ করার তথ্য (যদি সারি ব্লক করা হয়)।
  • অবজেক্ট টাইপ ( <T, flavor> ), যার মধ্যে HIDL-সংজ্ঞায়িত ধরনের সারি উপাদান এবং কিউ ফ্লেভার (সিঙ্ক্রোনাইজ বা আনসিঙ্ক্রোনাইজড) অন্তর্ভুক্ত থাকে।

আপনি একটি MessageQueue অবজেক্ট তৈরি করতে MQDescriptor অবজেক্ট ব্যবহার করতে পারেন:

MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)

resetPointers প্যারামিটার নির্দেশ করে যে এই MessageQueue অবজেক্ট তৈরি করার সময় রিড এবং রাইট পজিশন 0 এ রিসেট করতে হবে কিনা। একটি আনসিঙ্ক্রোনাইজড সারিতে, রিড পজিশন (যা আনসিঙ্ক্রোনাইজড সারিতে প্রতিটি MessageQueue অবজেক্টের স্থানীয়) তৈরির সময় সর্বদা 0 এ সেট করা থাকে। সাধারণত, প্রথম বার্তা সারি অবজেক্ট তৈরি করার সময় MQDescriptor শুরু করা হয়। শেয়ার্ড মেমরির উপর অতিরিক্ত নিয়ন্ত্রণের জন্য, আপনি ম্যানুয়ালি MQDescriptor সেট আপ করতে পারেন ( MQDescriptor system/libhidl/base/include/hidl/MQDescriptor.h এ সংজ্ঞায়িত করা হয়েছে), তারপর এই বিভাগে বর্ণিত প্রতিটি MessageQueue অবজেক্ট তৈরি করুন।

ব্লক সারি এবং ইভেন্ট পতাকা

ডিফল্টরূপে, সারি পড়া এবং লেখা ব্লক করা সমর্থন করে না। কল রিড এবং রাইট ব্লকিং দুই ধরনের আছে:

  • সংক্ষিপ্ত ফর্ম , তিনটি প্যারামিটার সহ (ডেটা পয়েন্টার, আইটেমের সংখ্যা, সময় শেষ), একটি একক সারিতে পৃথক পঠন এবং লেখার ক্রিয়াকলাপগুলিতে ব্লক করা সমর্থন করে। এই ফর্মটি ব্যবহার করার সময়, সারিটি ইভেন্ট ফ্ল্যাগ এবং বিটমাস্কগুলি অভ্যন্তরীণভাবে পরিচালনা করে এবং প্রথম বার্তা সারি অবজেক্টটি true দ্বিতীয় প্যারামিটার দিয়ে শুরু করতে হবে। যেমন:
    // For an unsynchronized FMQ that supports blocking
    mFmqUnsynchronizedBlocking =
      new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
          (kNumElementsInQueue, true /* enable blocking operations */);
    
  • দীর্ঘ ফর্ম , ছয়টি প্যারামিটার সহ (ইভেন্ট ফ্ল্যাগ এবং বিটমাস্ক সহ), একাধিক সারির মধ্যে একটি ভাগ করা EventFlag অবজেক্ট ব্যবহার করে সমর্থন করে এবং বিজ্ঞপ্তি বিট মাস্ক ব্যবহার করার অনুমতি দেয়। এই ক্ষেত্রে, ইভেন্ট ফ্ল্যাগ এবং বিটমাস্ক অবশ্যই প্রতিটি রিড এবং রাইট কলে সরবরাহ করতে হবে।

দীর্ঘ ফর্মের জন্য, আপনি প্রতিটি readBlocking() এবং writeBlocking() কলে স্পষ্টভাবে EventFlag সরবরাহ করতে পারেন। আপনি একটি অভ্যন্তরীণ ইভেন্ট ফ্ল্যাগ সহ সারিগুলির মধ্যে একটিকে আরম্ভ করতে পারেন, যেটিকে অবশ্যই সেই সারির MessageQueue অবজেক্ট থেকে getEventFlagWord() ব্যবহার করে বের করতে হবে এবং অন্যান্য FMQ-এর সাথে ব্যবহারের জন্য প্রতিটি প্রক্রিয়াতে একটি EventFlag অবজেক্ট তৈরি করতে ব্যবহার করা হবে। বিকল্পভাবে, আপনি যেকোনো উপযুক্ত শেয়ার্ড মেমরির সাথে EventFlag অবজেক্ট শুরু করতে পারেন।

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

মেমরিটিকে শুধুমাত্র পঠিত হিসাবে চিহ্নিত করুন

ডিফল্টরূপে, শেয়ার করা মেমরিতে পড়ার এবং লেখার অনুমতি রয়েছে। আনসিঙ্ক্রোনাইজড সারিগুলির জন্য ( kUnsynchronizedWrite ), লেখক MQDescriptorUnsync অবজেক্টগুলি হস্তান্তর করার আগে সমস্ত পাঠকদের জন্য লেখার অনুমতিগুলি সরাতে চাইতে পারেন। এটি নিশ্চিত করে যে অন্যান্য প্রক্রিয়াগুলি সারিতে লিখতে পারে না, যা পাঠক প্রক্রিয়াগুলিতে বাগ বা খারাপ আচরণ থেকে রক্ষা করার জন্য সুপারিশ করা হয়। লেখক যদি চান যে পাঠকরা যখনই সারির পঠিত দিক তৈরি করতে MQDescriptorUnsync ব্যবহার করে তখনই সারিটি পুনরায় সেট করতে সক্ষম হন, তাহলে মেমরিটি কেবল-পঠন হিসাবে চিহ্নিত করা যাবে না। এটি MessageQueue কনস্ট্রাক্টরের ডিফল্ট আচরণ। সুতরাং যদি এই সারির বিদ্যমান ব্যবহারকারী থাকে, resetPointer=false দিয়ে সারি তৈরি করতে তাদের কোড পরিবর্তন করতে হবে।

  • লেখক: একটি MQDescriptor ফাইল বর্ণনাকারীর সাথে ashmem_set_prot_region কল করুন এবং অঞ্চলটি শুধুমাত্র পঠনযোগ্য ( PROT_READ ):
    int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
  • পাঠক: resetPointer=false দিয়ে বার্তা সারি তৈরি করুন (ডিফল্টটি true ):
    mFmq = new (std::nothrow) MessageQueue(mqDesc, false);

MessageQueue ব্যবহার করুন

MessageQueue অবজেক্টের সর্বজনীন API হল:

size_t availableToWrite() // Space available (number of elements).
size_t availableToRead() // Number of elements available.
size_t getQuantumSize() // Size of type T in bytes.
size_t getQuantumCount() // Number of items of type T that fit in the FMQ.
bool isValid() // Whether the FMQ is configured correctly.
const MQDescriptor<T, flavor>* getDesc() // Return info to send to other process.

bool write(const T* data)  // Write one T to FMQ; true if successful.
bool write(const T* data, size_t count) // Write count T's; no partial writes.

bool read(T* data); // read one T from FMQ; true if successful.
bool read(T* data, size_t count); // Read count T's; no partial reads.

bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0);
bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0);

// Allows multiple queues to share a single event flag word
std::atomic<uint32_t>* getEventFlagWord();

bool writeBlocking(const T* data, size_t count, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos = 0,
android::hardware::EventFlag* evFlag = nullptr); // Blocking write operation for count Ts.

bool readBlocking(T* data, size_t count, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos = 0,
android::hardware::EventFlag* evFlag = nullptr) // Blocking read operation for count Ts;

// APIs to allow zero copy read/write operations
bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
bool commitWrite(size_t nMessages);
bool beginRead(size_t nMessages, MemTransaction* memTx) const;
bool commitRead(size_t nMessages);

একক অপারেশনে কতটা ডেটা স্থানান্তর করা যেতে পারে তা নির্ধারণ করতে আপনি availableToWrite() এবং availableToRead() ব্যবহার করতে পারেন। একটি অসিঙ্ক্রোনাইজড সারিতে:

  • availableToWrite() সর্বদা সারির ক্ষমতা প্রদান করে।
  • প্রতিটি পাঠকের নিজস্ব পড়ার অবস্থান রয়েছে এবং availableToRead() জন্য নিজস্ব গণনা করে।
  • ধীর পাঠকের দৃষ্টিকোণ থেকে, সারিটি উপচে পড়ার অনুমতি দেওয়া হয়; এর ফলে availableToRead() সারির আকারের চেয়ে বড় একটি মান ফেরত দিতে পারে। একটি ওভারফ্লো ব্যর্থ হওয়ার পরে প্রথম পঠিত হয় এবং সেই পাঠকের জন্য রিড পজিশন বর্তমান রাইট পয়েন্টারের সমান সেট করা হয়, ওভারফ্লোটি availableToRead() এর মাধ্যমে রিপোর্ট করা হয়েছিল কিনা।

read() এবং write() মেথডগুলো true হয় যদি অনুরোধ করা সমস্ত ডাটা কিউতে এবং থেকে স্থানান্তর করা যায়। এই পদ্ধতিগুলি ব্লক করে না; তারা হয় সফল (এবং true ফিরে), অথবা ব্যর্থতা ( false ) অবিলম্বে ফিরে.

readBlocking() এবং writeBlocking() পদ্ধতি অনুরোধ করা অপারেশন সম্পূর্ণ না হওয়া পর্যন্ত অপেক্ষা করে, অথবা তাদের সময় শেষ না হওয়া পর্যন্ত (0 এর একটি timeOutNanos মান মানে কখনই সময় শেষ হয় না)।

ব্লকিং অপারেশন একটি ইভেন্ট পতাকা শব্দ ব্যবহার করে প্রয়োগ করা হয়। ডিফল্টরূপে, প্রতিটি সারি readBlocking() এবং writeBlocking() এর সংক্ষিপ্ত রূপকে সমর্থন করার জন্য নিজস্ব পতাকা শব্দ তৈরি করে এবং ব্যবহার করে। একাধিক সারি একটি একক শব্দ ভাগ করতে পারে, যাতে একটি প্রক্রিয়া যে কোনো সারিতে লেখা বা পড়ার জন্য অপেক্ষা করতে পারে। getEventFlagWord() কল করার মাধ্যমে, আপনি একটি সারির ইভেন্ট ফ্ল্যাগ শব্দের একটি পয়েন্টার পেতে পারেন এবং আপনি সেই পয়েন্টারটি ব্যবহার করতে পারেন (অথবা একটি উপযুক্ত ভাগ করা মেমরি অবস্থানের জন্য যে কোনো পয়েন্টার) একটি EventFlag অবজেক্ট তৈরি করতে readBlocking() এর দীর্ঘ আকারে পাস করতে পারেন এবং একটি ভিন্ন সারির জন্য writeBlocking()readNotification এবং writeNotification প্যারামিটারগুলি বলে যে ইভেন্ট ফ্ল্যাগের কোন বিটগুলি সেই সারিতে পড়া এবং লেখার সংকেত দিতে ব্যবহার করা উচিত৷ readNotification এবং writeNotification হল 32-বিট বিটমাস্ক।

readBlocking() writeNotification বিটের উপর অপেক্ষা করে; যদি সেই প্যারামিটারটি 0 হয়, কলটি সর্বদা ব্যর্থ হয়। যদি readNotification মান 0 হয়, তবে কলটি ব্যর্থ হবে না, কিন্তু একটি সফল পঠন কোনো বিজ্ঞপ্তি বিট সেট করবে না। একটি সিঙ্ক্রোনাইজড সারিতে, এর মানে হল যে সংশ্লিষ্ট writeBlocking() কল কখনই জেগে ওঠে না যদি না বিটটি অন্য কোথাও সেট করা থাকে। একটি আনসিঙ্ক্রোনাইজড সারিতে, writeBlocking() অপেক্ষা করে না (এটি এখনও রাইট নোটিফিকেশন বিট সেট করার জন্য ব্যবহার করা উচিত), এবং এটি পড়ার জন্য উপযুক্ত কোনো বিজ্ঞপ্তি বিট সেট না করা। একইভাবে, readNotification 0 হলে writeblocking() ব্যর্থ হয়, এবং একটি সফল লেখা নির্দিষ্ট writeNotification বিট সেট করে।

একবারে একাধিক সারিতে অপেক্ষা করতে, একটি EventFlag অবজেক্টের wait() পদ্ধতি ব্যবহার করে বিজ্ঞপ্তির বিটমাস্কে অপেক্ষা করুন। wait() পদ্ধতিটি সেই বিটগুলির সাথে একটি স্ট্যাটাস শব্দ প্রদান করে যা জেগে ওঠা সেটের কারণ হয়। তারপরে এই তথ্যটি যাচাই করতে ব্যবহৃত হয় যে সংশ্লিষ্ট সারিতে পছন্দসই লেখা এবং পড়া অপারেশনের জন্য পর্যাপ্ত স্থান বা ডেটা রয়েছে এবং একটি ননব্লকিং write() এবং read() সম্পাদন করা হয়। একটি পোস্ট অপারেশন বিজ্ঞপ্তি পেতে, EventFlag অবজেক্টের wake() পদ্ধতিতে আরেকটি কল ব্যবহার করুন। EventFlag বিমূর্ততার একটি সংজ্ঞার জন্য, দেখুন system/libfmq/include/fmq/EventFlag.h

জিরো কপি অপারেশন

read , write , readBlocking , এবং writeBlocking() পদ্ধতিগুলি একটি আর্গুমেন্ট হিসাবে একটি ইনপুট-আউটপুট বাফারে একটি পয়েন্টার নেয় এবং একই এবং FMQ রিং বাফারের মধ্যে ডেটা অনুলিপি করতে অভ্যন্তরীণভাবে memcpy() কলগুলি ব্যবহার করে। কর্মক্ষমতা উন্নত করতে, অ্যান্ড্রয়েড 8.0 এবং উচ্চতর এপিআইগুলির একটি সেট অন্তর্ভুক্ত করে যা রিং বাফারে সরাসরি পয়েন্টার অ্যাক্সেস প্রদান করে, memcpy কল ব্যবহার করার প্রয়োজনীয়তা দূর করে।

শূন্য কপি FMQ অপারেশনের জন্য নিম্নলিখিত পাবলিক API ব্যবহার করুন:

bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
bool commitWrite(size_t nMessages);

bool beginRead(size_t nMessages, MemTransaction* memTx) const;
bool commitRead(size_t nMessages);
  • beginWrite পদ্ধতি FMQ রিং বাফারে বেস পয়েন্টার প্রদান করে। ডেটা লেখার পরে, এটি commitWrite() ব্যবহার করে কমিট করুন। beginRead এবং commitRead পদ্ধতি একইভাবে কাজ করে।
  • beginRead এবং Write পদ্ধতিগুলি পঠিত এবং লিখিত বার্তাগুলির সংখ্যা ইনপুট হিসাবে নেয় এবং একটি বুলিয়ান ফেরত দেয় যা বোঝায় যে পড়া বা লেখা সম্ভব কিনা। যদি পঠন বা লেখা সম্ভব হয়, memTx স্ট্রাকটটি বেস পয়েন্টার দিয়ে তৈরি করা হয় যা রিং বাফার শেয়ার্ড মেমরিতে সরাসরি পয়েন্টার অ্যাক্সেসের জন্য ব্যবহার করা যেতে পারে।
  • MemRegion স্ট্রাকটে মেমরির একটি ব্লকের বিশদ বিবরণ রয়েছে, যার মধ্যে রয়েছে বেস পয়েন্টার (মেমরি ব্লকের বেস অ্যাড্রেস) এবং T এর পরিপ্রেক্ষিতে দৈর্ঘ্য (বার্তা সারির HIDL-সংজ্ঞায়িত প্রকারের পরিপ্রেক্ষিতে মেমরি ব্লকের দৈর্ঘ্য)।
  • MemTransaction struct-এ দুটি MemRegion স্ট্রাকট রয়েছে, first এবং second রিং বাফারে পড়া বা লেখার জন্য সারির শুরুতে মোড়ানোর প্রয়োজন হতে পারে। এর অর্থ হল FMQ রিং বাফারে ডেটা পড়তে এবং লিখতে দুটি বেস পয়েন্টার প্রয়োজন।

একটি MemRegion struct থেকে বেস ঠিকানা এবং দৈর্ঘ্য পেতে:

T* getAddress(); // gets the base address
size_t getLength(); // gets the length of the memory region in terms of T
size_t getLengthInBytes(); // gets the length of the memory region in bytes

একটি MemTransaction অবজেক্টের মধ্যে প্রথম এবং দ্বিতীয় MemRegion স্ট্রাকচারের রেফারেন্স পেতে:

const MemRegion& getFirstRegion(); // get a reference to the first MemRegion
const MemRegion& getSecondRegion(); // get a reference to the second MemRegion

শূন্য অনুলিপি API ব্যবহার করে FMQ-তে উদাহরণ লিখুন:

MessageQueueSync::MemTransaction tx;
if (mQueue->beginRead(dataLen, &tx)) {
    auto first = tx.getFirstRegion();
    auto second = tx.getSecondRegion();

    foo(first.getAddress(), first.getLength()); // method that performs the data write
    foo(second.getAddress(), second.getLength()); // method that performs the data write

    if(commitWrite(dataLen) == false) {
       // report error
    }
} else {
   // report error
}

নিম্নলিখিত সহায়ক পদ্ধতিগুলিও MemTransaction এর অংশ:

  • T* getSlot(size_t idx); এই MemTransaction অবজেক্টের অংশ MemRegions এর মধ্যে স্লট idx এ একটি পয়েন্টার ফেরত দেয়। যদি MemTransaction অবজেক্ট T টাইপের N আইটেম পড়তে এবং লেখার জন্য মেমরি অঞ্চলগুলিকে প্রতিনিধিত্ব করে, তাহলে idx এর বৈধ পরিসীমা 0 এবং N-1-এর মধ্যে।
  • bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1); ইনডেক্স startIdx থেকে শুরু করে অবজেক্ট দ্বারা বর্ণিত মেমরি অঞ্চলে T টাইপের nMessages আইটেমগুলি লেখে। এই পদ্ধতিটি memcpy() ব্যবহার করে এবং শূন্য অনুলিপি অপারেশনের জন্য ব্যবহার করা বোঝায় না। যদি MemTransaction অবজেক্টটি T টাইপের N আইটেম পড়তে এবং লেখার জন্য মেমরির প্রতিনিধিত্ব করে, তাহলে idx এর বৈধ পরিসীমা 0 এবং N-1-এর মধ্যে।
  • bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1); startIdx থেকে শুরু করে অবজেক্ট দ্বারা বর্ণিত মেমরি অঞ্চল থেকে T টাইপের nMessages আইটেমগুলি পড়ার একটি সহায়ক পদ্ধতি। এই পদ্ধতিটি memcpy() ব্যবহার করে এবং এটি শূন্য কপি অপারেশনের জন্য ব্যবহার করা হয় না।

HIDL এর উপর সারি পাঠান

সৃষ্টির দিকে:

  1. উপরে বর্ণিত হিসাবে একটি বার্তা সারি অবজেক্ট তৈরি করুন।
  2. isValid() দিয়ে বস্তুটি বৈধ কিনা তা যাচাই করুন।
  3. আপনি যদি readBlocking() বা writeBlocking() এর দীর্ঘ আকারে EventFlag পাস করে একাধিক সারিতে অপেক্ষা করছেন, তাহলে আপনি একটি MessageQueue অবজেক্ট থেকে ইভেন্ট ফ্ল্যাগ পয়েন্টার ( getEventFlagWord() ব্যবহার করে ) বের করতে পারেন যা পতাকা তৈরি করতে শুরু করা হয়েছিল, এবং প্রয়োজনীয় EventFlag অবজেক্ট তৈরি করতে সেই পতাকাটি ব্যবহার করুন।
  4. একটি বর্ণনাকারী বস্তু পেতে MessageQueue পদ্ধতি getDesc() ব্যবহার করুন।
  5. HAL ফাইলে, পদ্ধতিটিকে fmq_sync টাইপের একটি প্যারামিটার দিন অথবা fmq_unsync যেখানে T একটি উপযুক্ত HIDL-সংজ্ঞায়িত প্রকার। getDesc() দ্বারা প্রত্যাবর্তিত বস্তুটি গ্রহণ প্রক্রিয়ায় পাঠাতে এটি ব্যবহার করুন।

প্রাপ্তির দিকে:

  1. একটি MessageQueue অবজেক্ট তৈরি করতে বর্ণনাকারী অবজেক্ট ব্যবহার করুন। একই সারি স্বাদ এবং ডেটা টাইপ ব্যবহার করুন, অথবা টেমপ্লেট কম্পাইল করতে ব্যর্থ হয়।
  2. আপনি যদি একটি ইভেন্ট পতাকা বের করে থাকেন, তাহলে রিসিভিং প্রক্রিয়ায় সংশ্লিষ্ট MessageQueue অবজেক্ট থেকে পতাকাটি বের করুন।
  3. ডেটা স্থানান্তর করতে MessageQueue অবজেক্ট ব্যবহার করুন।