फ़ास्ट मैसेज क्यू (FMQ)

HIDL का रिमोट प्रोसेस कॉल (आरपीसी) इंफ़्रास्ट्रक्चर, बाइंडर मेकेनिज्म का इस्तेमाल करता है. इसका मतलब है कि कॉल में ओवरहेड शामिल होता है, इसके लिए कर्नेल ऑपरेशन की ज़रूरत होती है, और यह शेड्यूलर ऐक्शन को ट्रिगर कर सकता है. हालांकि, जिन मामलों में डेटा को कम ओवरहेड और बिना कर्नेल के, प्रोसेस के बीच ट्रांसफ़र करना होता है उनमें फ़ास्ट मैसेज क्यू (एफ़एमक्यू) सिस्टम का इस्तेमाल किया जाता है.

FMQ, पसंदीदा प्रॉपर्टी के साथ मैसेज की कतारें बनाता है. HIDL आरपीसी कॉल के ज़रिए, MQDescriptorSync या MQDescriptorUnsync ऑब्जेक्ट भेजा जा सकता है. मैसेज कतार को ऐक्सेस करने के लिए, मैसेज पाने वाली प्रोसेस में इस ऑब्जेक्ट का इस्तेमाल किया जाता है.

सूची के टाइप

Android, दो तरह की सूची (इन्हें फ़्लेवर कहा जाता है) के साथ काम करता है:

  • सिंक नहीं की गई कतारें ओवरफ़्लो हो सकती हैं और उनमें कई रीडर हो सकते हैं. हर रीडर को समय पर डेटा पढ़ना होगा, नहीं तो वह डेटा मिट जाएगा.
  • सिंक की गई सूचियों में आइटम की संख्या ज़्यादा नहीं हो सकती. साथ ही, इनमें सिर्फ़ एक रीडर हो सकता है.

दोनों तरह की सूचियों में डेटा कम नहीं होना चाहिए. खाली सूची से डेटा पढ़ने की कोशिश करने पर, सूची में डेटा नहीं मिलता. साथ ही, इनमें सिर्फ़ एक व्यक्ति डेटा लिख सकता है.

सिंक नहीं की गई कतारें

सिंक न की गई सूची में सिर्फ़ एक लेखक हो सकता है. हालांकि, इसमें एक से ज़्यादा पाठक हो सकते हैं. सूची के लिए, लिखने की एक जगह होती है. हालांकि, हर रीडर अपनी पढ़ने की जगह का ट्रैक रखता है.

सूची में डेटा डालने की प्रोसेस हमेशा पूरी होती है. हालांकि, अगर डेटा, सूची की तय की गई क्षमता से ज़्यादा है, तो डेटा डालने की प्रोसेस तुरंत बंद हो जाती है. हर रीडर के पास डेटा पढ़ने के लिए अलग-अलग पोज़िशन हो सकती है. इसलिए, हर रीडर के डेटा को पढ़ने का इंतज़ार करने के बजाय, जब भी नए डेटा को स्टोर करने के लिए जगह की ज़रूरत होती है, तो डेटा को सूची से हटा दिया जाता है.

डेटा को सूची में सबसे आखिर में शामिल होने से पहले, उसे वापस पाने की ज़िम्मेदारी पाठकों की होती है. अगर कोई रीड, उपलब्ध डेटा से ज़्यादा डेटा पढ़ने की कोशिश करता है, तो वह तुरंत विफल हो जाता है (अगर वह नॉन-ब्लॉकिंग है) या ज़रूरत के मुताबिक डेटा उपलब्ध होने का इंतज़ार करता है (अगर वह ब्लॉकिंग है). अगर कोई रीड, सूची की क्षमता से ज़्यादा डेटा पढ़ने की कोशिश करता है, तो वह हमेशा तुरंत विफल हो जाता है.

अगर कोई रीडर, लिखने वाले के साथ नहीं चल पाता है, जैसे कि लिखा गया और अब तक रीडर से न पढ़ा गया डेटा, सूची की क्षमता से ज़्यादा हो जाता है, तो अगली बार पढ़ने पर डेटा नहीं दिखता. इसके बजाय, यह रीडर की पढ़ी गई पोज़िशन को, लिखने की पोज़िशन के साथ-साथ क्षमता का आधा हिस्सा तक रीसेट कर देता है. इसके बाद, 'कार्रवाई पूरी नहीं हुई' मैसेज दिखता है. इससे, आधा बफ़र पढ़ने के लिए उपलब्ध रहता है और नए डेटा को लिखने के लिए जगह सुरक्षित रहती है, ताकि सूची फिर से तुरंत भर न जाए. अगर पढ़ने के लिए उपलब्ध डेटा को ओवरफ़्लो के बाद, लेकिन अगले पढ़ने से पहले देखा जाता है, तो यह सूची में मौजूद डेटा की क्षमता से ज़्यादा डेटा दिखाता है. इससे पता चलता है कि ओवरफ़्लो हुआ है. (अगर उपलब्ध डेटा की जांच करने और उस डेटा को पढ़ने के बीच, सूची में डेटा की संख्या तय सीमा से ज़्यादा हो जाती है, तो डेटा पढ़ने में हुई गड़बड़ी का पता चलता है.)

का इस्तेमाल करना चाहिए.

सिंक की गई कतारें

सिंक की गई सूची में एक लेखक और एक पाठक होता है. इसमें लिखने और पढ़ने की एक ही स्थिति होती है. सूची में जितने डेटा के लिए जगह है उससे ज़्यादा डेटा नहीं लिखा जा सकता. इसके अलावा, सूची में मौजूद डेटा से ज़्यादा डेटा नहीं पढ़ा जा सकता. उपलब्ध जगह या डेटा से ज़्यादा डेटा डालने की कोशिश करने पर, ब्लॉकिंग या नॉन-ब्लॉकिंग रीड या लिखने वाले फ़ंक्शन को कॉल करने के आधार पर, तुरंत गड़बड़ी का मैसेज दिखता है या तब तक ब्लॉक किया जाता है, जब तक कि वह कार्रवाई पूरी नहीं हो जाती. सूची की क्षमता से ज़्यादा डेटा पढ़ने या उसमें बदलाव करने की कोशिश हमेशा तुरंत विफल हो जाती है.

एफ़एमक्यू सेट अप करना

मैसेज क्यू के लिए कई 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) initializer, मैसेज कतार की सुविधा के साथ काम करने वाला ऑब्जेक्ट बनाता और शुरू करता है.
  • MessageQueue<T, flavor>(numElements, configureEventFlagWord) initializer, एक ऑब्जेक्ट बनाता है और उसे शुरू करता है. यह ऑब्जेक्ट, ब्लॉकिंग के साथ मैसेज कतार की सुविधा के साथ काम करता है.
  • flavor, सिंक की गई सूची के लिए kSynchronizedReadWrite या सिंक नहीं की गई सूची के लिए kUnsynchronizedWrite हो सकता है.
  • इस उदाहरण में, uint16_t कोई भी HIDL से तय किया गया टाइप हो सकता है. इसमें नेस्ट किए गए बफ़र (string या vec टाइप नहीं), हैंडल या इंटरफ़ेस शामिल नहीं होने चाहिए.
  • kNumElementsInQueue, एंट्री की संख्या में सूची के साइज़ को दिखाता है. यह सूची के लिए, शेयर किए गए मेमोरी बफ़र का साइज़ तय करता है.

दूसरा MessageQueue ऑब्जेक्ट बनाना

मैसेज की सूची का दूसरा हिस्सा, पहले हिस्से से मिले MQDescriptor ऑब्जेक्ट का इस्तेमाल करके बनाया जाता है. MQDescriptor ऑब्जेक्ट को HIDL या AIDL आरपीसी कॉल की मदद से, उस प्रोसेस पर भेजा जाता है जिसमें मैसेज कतार का दूसरा हिस्सा होता है. 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 को साफ़ तौर पर दिया जा सकता है. किसी एक कतार को किसी इंटरनल इवेंट फ़्लैग की मदद से शुरू किया जा सकता है. इसके बाद, getEventFlagWord() का इस्तेमाल करके उस कतार के MessageQueue ऑब्जेक्ट से इसे निकाला जाना चाहिए. साथ ही, अन्य एफ़एमक्यू के साथ इस्तेमाल करने के लिए, हर प्रोसेस में EventFlag ऑब्जेक्ट बनाने के लिए इसका इस्तेमाल किया जाना चाहिए. इसके अलावा, किसी भी सही शेयर की गई मेमोरी का इस्तेमाल करके, EventFlag ऑब्जेक्ट को शुरू किया जा सकता है.

आम तौर पर, हर कतार में सिर्फ़ एक तरह की पाबंदी का इस्तेमाल किया जाना चाहिए. जैसे, नॉन-ब्लॉकिंग, शॉर्ट-फ़ॉर्म ब्लॉकिंग या लॉन्ग-फ़ॉर्म ब्लॉकिंग. इन्हें एक साथ इस्तेमाल करना गलत नहीं है. हालांकि, मनमुताबिक नतीजा पाने के लिए, सावधानी से प्रोग्रामिंग करना ज़रूरी है.

मेमोरी को रीड-ओनली के तौर पर मार्क करना

डिफ़ॉल्ट रूप से, शेयर की गई मेमोरी में डेटा को पढ़ने और उसमें बदलाव करने की अनुमतियां होती हैं. सिंक नहीं की गई सूचियों (kUnsynchronizedWrite) के लिए, हो सकता है कि लेखक MQDescriptorUnsync ऑब्जेक्ट को डिलीवर करने से पहले, सभी पाठकों के लिए लिखने की अनुमतियां हटाना चाहे. इससे यह पक्का होता है कि अन्य प्रोसेस, सूची में डेटा नहीं डाल सकतीं. हमारा सुझाव है कि आप रीडर प्रोसेस में गड़बड़ियों या गलत व्यवहार से बचने के लिए, ऐसा करें. अगर लेखक चाहता है कि पाठक, सूची के रीड साइड को बनाने के लिए MQDescriptorUnsync का इस्तेमाल करते समय, सूची को रीसेट कर सकें, तो मेमोरी को रीड-ओनली के तौर पर मार्क नहीं किया जा सकता. यह MessageQueue कंस्ट्रक्टर का डिफ़ॉल्ट तरीका है. इसलिए, अगर इस सूची के मौजूदा उपयोगकर्ता हैं, तो resetPointer=false की मदद से सूची बनाने के लिए, उनके कोड में बदलाव करना होगा.

  • लेखक: MQDescriptor फ़ाइल डिस्क्रिप्टर और रीड-ओनली (PROT_READ) पर सेट किए गए क्षेत्र के साथ ashmem_set_prot_region को कॉल करें:
    int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
  • रीडर: resetPointer=false का इस्तेमाल करके मैसेज की सूची बनाएं (डिफ़ॉल्ट तौर पर true है):
    mFmq = new (std::nothrow) MessageQueue(mqDesc, false);

MessageQueue का इस्तेमाल करना

MessageQueue ऑब्जेक्ट का सार्वजनिक एपीआई:

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() तरीके तब तक इंतज़ार करते हैं, जब तक अनुरोध की गई कार्रवाई पूरी नहीं हो जाती या जब तक वे टाइम आउट नहीं हो जाते (timeOutNanos की वैल्यू 0 होने का मतलब है कि कभी टाइम आउट नहीं होगा).

ब्लॉक करने की कार्रवाइयां, इवेंट फ़्लैग शब्द का इस्तेमाल करके लागू की जाती हैं. डिफ़ॉल्ट रूप से, हर कतार अपने फ़्लैग वर्ड बनाती है और उसका इस्तेमाल करती है, ताकि 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() तरीकों में, इनपुट-आउटपुट बफ़र के पॉइंटर को आर्ग्युमेंट के तौर पर इस्तेमाल किया जाता है. साथ ही, इन तरीकों में memcpy() कॉल का इस्तेमाल करके, इनपुट-आउटपुट बफ़र और एफ़एमक्यू रिंग बफ़र के बीच डेटा कॉपी किया जाता है. परफ़ॉर्मेंस को बेहतर बनाने के लिए, Android 8.0 और उसके बाद के वर्शन में एपीआई का एक सेट शामिल किया गया है. यह सेट, रिंग बफ़र में डायरेक्ट पॉइंटर ऐक्सेस उपलब्ध कराता है. इससे memcpy कॉल का इस्तेमाल करने की ज़रूरत नहीं पड़ती.

शून्य कॉपी वाले एफ़एमक्यू ऑपरेशन के लिए, इन सार्वजनिक एपीआई का इस्तेमाल करें:

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 struct को बेस पॉइंटर से पॉप्युलेट किया जाता है. इनका इस्तेमाल, रिंग बफ़र शेयर की गई मेमोरी में सीधे पॉइंटर ऐक्सेस करने के लिए किया जा सकता है.
  • MemRegion स्ट्रक्चर में मेमोरी के ब्लॉक की जानकारी होती है. इसमें बेस पॉइंटर (मेमोरी ब्लॉक का बेस पता) और T के हिसाब से लंबाई (मैसेज कतार के HIDL-तय किए गए टाइप के हिसाब से मेमोरी ब्लॉक की लंबाई) शामिल होती है.
  • MemTransaction स्ट्रक्चर में दो MemRegion स्ट्रक्चर, first और second होते हैं. ऐसा इसलिए, क्योंकि रिंग बफ़र में पढ़ने या लिखने के लिए, सूची की शुरुआत में फिर से शुरू करने की ज़रूरत पड़ सकती है. इसका मतलब है कि एफ़एमक्यू रिंग बफ़र में डेटा पढ़ने और उसमें डेटा लिखने के लिए, दो बेस पॉइंटर की ज़रूरत होती है.

MemRegion स्ट्रक्चर से बेस पता और लंबाई पाने के लिए:

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

जीरो कॉपी एपीआई का इस्तेमाल करके, एफएमक्यू में डेटा लिखने का उदाहरण:

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);, MemRegions में मौजूद स्लॉट idx का पॉइंटर दिखाता है, जो इस MemTransaction ऑब्जेक्ट का हिस्सा है. अगर MemTransaction ऑब्जेक्ट, T टाइप के N आइटम को पढ़ने और उनमें बदलाव करने के लिए, मेमोरी क्षेत्रों को दिखा रहा है, तो idx की मान्य रेंज 0 से N-1 के बीच है.
  • bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);, ऑब्जेक्ट के बताए गए मेमोरी क्षेत्रों में, nMessages टाइप के T आइटम लिखता है. यह इंडेक्स startIdx से शुरू होता है. इस तरीके में 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 पास करके, कई कतारें इंतज़ार में हैं, तो getEventFlagWord() का इस्तेमाल करके, MessageQueue ऑब्जेक्ट से इवेंट फ़्लैग पॉइंटर निकाला जा सकता है. यह ऑब्जेक्ट, फ़्लैग बनाने के लिए शुरू किया गया था. साथ ही, ज़रूरी EventFlag ऑब्जेक्ट बनाने के लिए, उस फ़्लैग का इस्तेमाल किया जा सकता है.
  4. डिस्क्रिप्टर ऑब्जेक्ट पाने के लिए, MessageQueue तरीके getDesc() का इस्तेमाल करें.
  5. HAL फ़ाइल में, पैरामीटर के तौर पर fmq_sync या fmq_unsync का इस्तेमाल करें. यहां T, HIDL के मुताबिक तय किया गया सही टाइप है. getDesc() से वापस मिला ऑब्जेक्ट, पाने की प्रोसेस में भेजने के लिए इसका इस्तेमाल करें.

ईमेल पाने वाले के लिए:

  1. MessageQueue ऑब्जेक्ट बनाने के लिए, डिस्क्रिप्टर ऑब्जेक्ट का इस्तेमाल करें. एक ही कतार के फ़्लेवर और डेटा टाइप का इस्तेमाल करें. ऐसा न करने पर, टेंप्लेट को संकलित नहीं किया जा सकेगा.
  2. अगर आपने कोई इवेंट फ़्लैग निकाला है, तो उसे पाने की प्रोसेस में, उससे जुड़े MessageQueue ऑब्जेक्ट से फ़्लैग निकालें.
  3. डेटा ट्रांसफ़र करने के लिए, MessageQueue ऑब्जेक्ट का इस्तेमाल करें.