तेज़ी से मैसेज की सूची (FMQ)

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

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

सूची के टाइप

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

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

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

सिंक नहीं की गई सूचियां

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

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

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

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

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

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

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

मैसेज सूची में एक से ज़्यादा MessageQueue ऑब्जेक्ट होने चाहिए: एक ऑब्जेक्ट को लिखना होगा और एक या एक से ज़्यादा ऑब्जेक्ट पढ़ने होंगे. इसमें यह साफ़ तौर पर नहीं बताया गया है कि डेटा को पढ़ने या लिखने के लिए किस ऑब्जेक्ट का इस्तेमाल किया जाए. यह पक्का करना उपयोगकर्ता की ज़िम्मेदारी है कि डेटा को पढ़ने और लिखने के लिए, किसी एक ऑब्जेक्ट का इस्तेमाल न किया जाए. साथ ही, सिंक की गई सूचियों के लिए, एक से ज़्यादा पाठक न हों.

पहला MessageQ ऑब्जेक्ट बनाएं

मैसेज सूची को एक ही कॉल से बनाया और कॉन्फ़िगर किया जाता है:

#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) शुरू करने वाला टूल एक ऐसा ऑब्जेक्ट बनाता और शुरू करता है जो ब्लॉक करने के साथ मैसेज सूची की सुविधा के साथ काम करता है.
  • 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 ऑब्जेक्ट का सार्वजनिक एपीआई यह है:

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() तरीकों में, इनपुट-आउटपुट बफ़र के पॉइंटर को आर्ग्युमेंट के तौर पर इस्तेमाल किया जाता है. साथ ही, इन तरीकों में 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);, ऑब्जेक्ट के बताए गए मेमोरी क्षेत्रों में, इंडेक्स 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 पास करके, कई कतारें इंतज़ार में हैं, तो getEventFlagWord() का इस्तेमाल करके, MessageQueue ऑब्जेक्ट से इवेंट फ़्लैग पॉइंटर निकाला जा सकता है. यह ऑब्जेक्ट, फ़्लैग बनाने के लिए शुरू किया गया था. साथ ही, ज़रूरी EventFlag ऑब्जेक्ट बनाने के लिए, उस फ़्लैग का इस्तेमाल किया जा सकता है.
  4. डिस्क्रिप्टर ऑब्जेक्ट पाने के लिए, MessageQueue तरीके getDesc() का इस्तेमाल करें.
  5. HAL फ़ाइल में, तरीके को fmq_sync या fmq_unsync टाइप का पैरामीटर दें, जहां T सही HIDL-तय टाइप है. इसका इस्तेमाल getDesc() से मिले ऑब्जेक्ट को पाने की प्रोसेस में भेजने के लिए करें.

दाईं ओर:

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