void SendIPCMessage(ipc_message_t msg, bool blocking) { MessageQueue* q = msg.ConsumableEarly() ? ipcEarlyMessageQueue : ipcMessageQueue; msg.blocking = blocking; #ifdef IPC_DEBUG lk_lock(printing_lock, 1); std::cerr << "[SEND] IPC message, ID: " << msg.id << std::endl; lk_unlock(printing_lock); #endif lk_lock(lk_ipcMessageQueue, 1); q->push_back(msg); if (blocking) ackMessages->operator[](msg) = false; lk_unlock(lk_ipcMessageQueue); if (blocking) { lk_lock(lk_ipcMessageQueue, 1); while (ackMessages->at(msg) == false) { lk_unlock(lk_ipcMessageQueue); xio_sleep(10); lk_lock(lk_ipcMessageQueue, 1); } lk_unlock(lk_ipcMessageQueue); } }
void RPCChannel::OnMessageReceivedFromLink(const Message& msg) { AssertLinkThread(); mMonitor->AssertCurrentThreadOwns(); if (MaybeInterceptSpecialIOMessage(msg)) return; // regardless of the RPC stack, if we're awaiting a sync reply, we // know that it needs to be immediately handled to unblock us. if (AwaitingSyncReply() && msg.is_sync()) { // wake up worker thread waiting at SyncChannel::Send mRecvd = msg; NotifyWorkerThread(); return; } MessageQueue *queue = (msg.priority() == IPC::Message::PRIORITY_HIGH) ? &mUrgent : &mPending; bool compressMessage = (msg.compress() && !queue->empty() && queue->back().type() == msg.type() && queue->back().routing_id() == msg.routing_id()); if (compressMessage) { // This message type has compression enabled, and the back of // the queue was the same message type and routed to the same // destination. Replace it with the newer message. MOZ_ASSERT(queue->back().compress()); queue->pop_back(); } queue->push_back(msg); // There are three cases we're concerned about, relating to the state of // the main thread: // // (1) We are waiting on a sync reply - main thread is blocked on the IPC monitor. // - If the message is high priority, we wake up the main thread to // deliver the message. Otherwise, we leave it in the mPending queue, // posting a task to the main event loop, where it will be processed // once the synchronous reply has been received. // // (2) We are waiting on an RPC reply - main thread is blocked on the IPC monitor. // - Always wake up the main thread to deliver the message. // // (3) We are not waiting on a reply. // - We post a task to the main event loop. // bool waiting_rpc = (0 != StackDepth()); bool urgent = (msg.priority() == IPC::Message::PRIORITY_HIGH); if (waiting_rpc || (AwaitingSyncReply() && urgent)) { // Always wake up our RPC waiter, and wake up sync waiters for urgent // messages. NotifyWorkerThread(); } else { // Worker thread is either not blocked on a reply, or this is an // incoming RPC that raced with outgoing sync and needs to be deferred // to a later event-loop iteration. if (!compressMessage) { // If we compressed away the previous message, we'll reuse // its pending task. mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask)); } } }
/******************************* test functions *******************************/ int32_t ut_MessageQueue_simple(void) { TestCaseBegin(); std::mutex mMut; std::atomic_int mInt; mInt = 0; std::atomic_char running; running = 1; MessageQueue < std::function < int(void) >> messages; std::thread t1([&] { while(running) { const auto f = messages.receive(); if(f) mInt = f(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } ); CHECK(0 == mInt); messages.push_back([ = ] {return 1; } ); std::this_thread::sleep_for(std::chrono::milliseconds(20)); CHECK(1 == mInt); messages.push_back([ = ] {return 2; } ); std::this_thread::sleep_for(std::chrono::milliseconds(20)); CHECK(2 == mInt); messages.push_back([ = ] {return 4; } ); messages.push_front([ = ] {return 3; } ); std::this_thread::sleep_for(std::chrono::milliseconds(5)); CHECK(3 == mInt); std::this_thread::sleep_for(std::chrono::milliseconds(10)); CHECK(4 == mInt); { std::lock_guard<std::mutex> lg(mMut); messages.push_back([ = ] {return 5; } ); messages.push_back([ = ] {return 5; } ); messages.push_back([ = ] {return 5; } ); messages.clear_and_push_front([ = ] {return 6; } ); } std::this_thread::sleep_for(std::chrono::milliseconds(20)); CHECK(6 == mInt); std::this_thread::sleep_for(std::chrono::milliseconds(10)); CHECK(6 == mInt); messages.push_back([ = ] {return 5; } ); running = 0; t1.join(); TestCaseEnd(); }
int32_t ut_MessageQueue_complex(void) { TestCaseBegin(); std::mutex mMut; std::atomic_int mInt; mInt = 0; MessageQueue < std::pair < std::function<int(void)>, bool >> messages; std::thread t1([&] { while(true) { const auto f = messages.receive(); if(f.second) break; if(f.first) mInt = f.first(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } ); CHECK(0 == mInt); messages.push_back(std::make_pair([ = ] {return 1; }, false)); std::this_thread::sleep_for(std::chrono::milliseconds(100)); CHECK(1 == mInt); messages.push_back(std::make_pair([ = ] {return 2; }, false)); std::this_thread::sleep_for(std::chrono::milliseconds(100)); CHECK(2 == mInt); messages.push_back(std::make_pair([ = ] {return 4; }, false)); messages.push_back(std::make_pair([ = ] {return 5; }, false)); messages.push_front(std::make_pair([ = ] {return 3; }, false)); std::this_thread::sleep_for(std::chrono::milliseconds(40)); CHECK(3 == mInt); std::this_thread::sleep_for(std::chrono::milliseconds(50)); CHECK(4 == mInt); std::this_thread::sleep_for(std::chrono::milliseconds(50)); CHECK(5 == mInt); { std::lock_guard<std::mutex> lg(mMut); messages.push_back(std::make_pair([ = ] {return 4; }, false)); messages.push_back(std::make_pair([ = ] {return 4; }, false)); messages.push_back(std::make_pair([ = ] {return 4; }, false)); messages.clear_and_push_front(std::make_pair([ = ] {return 6; }, false)); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); CHECK(6 == mInt); std::this_thread::sleep_for(std::chrono::milliseconds(50)); CHECK(6 == mInt); messages.push_front(std::make_pair([ = ] {return 0xff; }, true)); std::this_thread::sleep_for(std::chrono::milliseconds(10)); t1.join(); TestCaseEnd(); }