Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/assert.h" | 5 #include "vm/assert.h" |
| 6 #include "vm/port.h" | 6 #include "vm/message_queue.h" |
| 7 #include "vm/unit_test.h" | 7 #include "vm/unit_test.h" |
| 8 | 8 |
| 9 namespace dart { | 9 namespace dart { |
| 10 | 10 |
| 11 TEST_CASE(Port) { | 11 |
| 12 const char* msg_data = "Hallo Velo!"; | 12 // Provide access to private members of MessageQueue for testing. |
| 13 | 13 class MessageQueueTestPeer { |
| 14 intptr_t port1 = PortMap::CreatePort(); | 14 public: |
| 15 intptr_t port2 = PortMap::CreatePort(); | 15 explicit MessageQueueTestPeer(MessageQueue* queue) : queue_(queue) {} |
| 16 EXPECT(port1 != port2); | 16 |
| 17 | 17 bool HasMessage() const { return queue_->head_ != NULL; } |
| 18 PortMessage* msg1 = new PortMessage(port1, 0, strdup(msg_data)); | 18 |
| 19 EXPECT_EQ(true, PortMap::PostMessage(msg1)); | 19 private: |
| 20 PortMessage* msg = PortMap::ReceiveMessage(10); | 20 MessageQueue* queue_; |
| 21 EXPECT_EQ(port1, msg->dest_id()); | 21 }; |
| 22 EXPECT_EQ(msg1, msg); | 22 |
| 23 | |
| 24 TEST_CASE(MessageQueue_BasicOperations) { | |
| 25 MessageQueue queue; | |
| 26 MessageQueueTestPeer queue_peer(&queue); | |
| 27 EXPECT(!queue_peer.HasMessage()); | |
| 28 | |
| 29 Dart_Port port = 1; | |
| 30 | |
| 31 // Add two messages. | |
| 32 PortMessage* msg1 = new PortMessage(port, 0, strdup("msg1")); | |
| 33 queue.Enqueue(msg1); | |
| 34 EXPECT(queue_peer.HasMessage()); | |
| 35 | |
| 36 PortMessage* msg2 = new PortMessage(port, 0, strdup("msg2")); | |
| 37 queue.Enqueue(msg2); | |
| 38 EXPECT(queue_peer.HasMessage()); | |
| 39 | |
| 40 // Remove two messages. | |
| 41 PortMessage* msg = queue.Dequeue(0); | |
| 42 EXPECT(msg != NULL); | |
| 43 EXPECT_STREQ("msg1", reinterpret_cast<char*>(msg->data())); | |
| 44 EXPECT(queue_peer.HasMessage()); | |
| 45 | |
| 46 msg = queue.Dequeue(0); | |
| 47 EXPECT(msg != NULL); | |
| 48 EXPECT_STREQ("msg2", reinterpret_cast<char*>(msg->data())); | |
| 49 EXPECT(!queue_peer.HasMessage()); | |
| 50 | |
| 23 delete msg1; | 51 delete msg1; |
| 24 | 52 delete msg2; |
| 25 msg1 = new PortMessage(port2, 0, strdup(msg_data)); | 53 } |
| 26 EXPECT_EQ(true, PortMap::PostMessage(msg1)); | 54 |
| 27 msg = PortMap::ReceiveMessage(10); | 55 |
| 28 EXPECT_EQ(port2, msg->dest_id()); | 56 // A thread which receives an expected sequence of messages. |
| 29 EXPECT_EQ(msg1, msg); | 57 static Monitor* sync = NULL; |
| 30 delete msg1; | 58 static MessageQueue* shared_queue = NULL; |
| 31 | 59 void MessageReceiver_start(uword unused) { |
| 32 PortMap::ClosePort(port1); | 60 // We only need an isolate here because the MonitorLocker in the |
| 33 EXPECT_EQ(false, PortMap::IsActivePort(port1)); | 61 // MessageQueue expects it. |
| 34 msg1 = new PortMessage(port1, 0, strdup(msg_data)); | |
| 35 EXPECT_EQ(false, PortMap::PostMessage(msg1)); | |
| 36 delete msg1; | |
| 37 EXPECT(PortMap::ReceiveMessage(10) == NULL); | |
| 38 | |
| 39 EXPECT_EQ(true, PortMap::IsActivePort(port2)); | |
| 40 msg1 = new PortMessage(port2, 0, strdup(msg_data)); | |
| 41 EXPECT_EQ(true, PortMap::PostMessage(msg1)); | |
| 42 PortMap::ClosePort(port2); | |
| 43 EXPECT(PortMap::ReceiveMessage(10) == NULL); | |
| 44 | |
| 45 for (int i = 0; i < 32; i++) { | |
| 46 intptr_t port = PortMap::CreatePort(); | |
| 47 PortMap::ClosePort(port); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 | |
| 52 // End-of-test marker. | |
| 53 static const intptr_t kEOT = 0xFFFF; | |
| 54 | |
| 55 void* AllocIntData(intptr_t payload) { | |
| 56 intptr_t* result = reinterpret_cast<intptr_t*>(malloc(sizeof(payload))); | |
| 57 *result = payload; | |
| 58 return result; | |
| 59 } | |
| 60 | |
| 61 | |
| 62 intptr_t GetIntData(void* data) { | |
| 63 return *reinterpret_cast<intptr_t*>(data); | |
| 64 } | |
| 65 | |
| 66 | |
| 67 void ThreadedPort_start(uword parameter) { | |
| 68 Dart::CreateIsolate(NULL, NULL); | 62 Dart::CreateIsolate(NULL, NULL); |
| 69 | 63 |
| 70 intptr_t remote = parameter; | 64 // Create a message queue and share it. |
| 71 intptr_t local = PortMap::CreatePort(); | 65 MessageQueue* queue = new MessageQueue(); |
| 72 | 66 shared_queue = queue; |
| 73 PortMap::PostMessage(new PortMessage(remote, 0, AllocIntData(local))); | 67 |
| 74 | 68 // Tell the other thread to fill the queue a bit. |
| 75 intptr_t count = 0; | 69 { |
| 76 while (true) { | 70 MonitorLocker ml(sync); |
| 77 PortMessage* msg = PortMap::ReceiveMessage(0); | 71 ml.Notify(); |
| 78 EXPECT_EQ(local, msg->dest_id()); | 72 } |
| 73 | |
| 74 // Wait for the other thread to fill the queue a bit. | |
| 75 { | |
| 76 MonitorLocker ml(sync); | |
| 77 ml.Wait(0); | |
| 78 } | |
| 79 | |
| 80 for (int i = 0; i < 3; i++) { | |
| 81 PortMessage* msg = queue->Dequeue(0); | |
| 79 EXPECT(msg != NULL); | 82 EXPECT(msg != NULL); |
| 80 if (GetIntData(msg->data()) == kEOT) { | 83 EXPECT_EQ(i+10, msg->dest_port()); |
| 81 break; | 84 EXPECT_EQ(i+100, msg->reply_port()); |
| 82 } | 85 EXPECT_EQ(i+1000, *(reinterpret_cast<int*>(msg->data()))); |
| 83 EXPECT(GetIntData(msg->data()) == count); | |
| 84 delete msg; | 86 delete msg; |
| 85 PortMap::PostMessage(new PortMessage(remote, 0, AllocIntData(count * 2))); | 87 } |
| 86 count++; | 88 for (int i = 0; i < 3; i++) { |
| 87 } | 89 PortMessage* msg = queue->Dequeue(0); |
| 88 PortMap::PostMessage(new PortMessage(remote, 0, AllocIntData(kEOT))); | 90 EXPECT(msg != NULL); |
| 89 | 91 EXPECT_EQ(i+20, msg->dest_port()); |
| 92 EXPECT_EQ(i+200, msg->reply_port()); | |
| 93 EXPECT_EQ(i+2000, *(reinterpret_cast<int*>(msg->data()))); | |
| 94 delete msg; | |
| 95 } | |
| 96 shared_queue = NULL; | |
| 97 delete queue; | |
| 90 Dart::ShutdownIsolate(); | 98 Dart::ShutdownIsolate(); |
| 91 } | 99 } |
| 92 | 100 |
| 93 | 101 |
| 94 TEST_CASE(ThreadedPort) { | 102 TEST_CASE(MessageQueue_WaitNotify) { |
| 95 intptr_t local = PortMap::CreatePort(); | 103 Thread* thread = new Thread(MessageReceiver_start, 0); |
| 96 | 104 EXPECT(thread != NULL); |
| 97 Thread* thr = new Thread(ThreadedPort_start, local); | 105 |
| 98 EXPECT(thr != NULL); | 106 // Wait for the shared queue to be created. |
| 99 | 107 sync = new Monitor(); |
| 100 PortMessage* msg = PortMap::ReceiveMessage(0); | 108 { |
| 101 EXPECT_EQ(local, msg->dest_id()); | 109 MonitorLocker ml(sync); |
| 110 ml.Wait(0); | |
| 111 } | |
| 112 ASSERT(shared_queue != NULL); | |
| 113 | |
| 114 // Pile up three messages before the other thread runs. | |
| 115 for (int i = 0; i < 3; i++) { | |
| 116 int* data = reinterpret_cast<int*>(malloc(sizeof(*data))); | |
| 117 *data = i+1000; | |
| 118 PortMessage* msg = new PortMessage(i+10, i+100, data); | |
| 119 shared_queue->Enqueue(msg); | |
| 120 } | |
| 121 | |
| 122 // Wake the other thread and have it start consuming messages. | |
| 123 { | |
| 124 MonitorLocker ml(sync); | |
| 125 ml.Notify(); | |
| 126 } | |
| 127 | |
| 128 // Add a few more messages after sleeping to allow the other thread | |
| 129 // to potentially exercise the blocking code path in Dequeue. | |
| 130 OS::Sleep(5); | |
| 131 for (int i = 0; i < 3; i++) { | |
| 132 int* data = reinterpret_cast<int*>(malloc(sizeof(*data))); | |
| 133 *data = i+2000; | |
| 134 PortMessage* msg = new PortMessage(i+20, i+200, data); | |
| 135 shared_queue->Enqueue(msg); | |
| 136 } | |
| 137 | |
| 138 sync = NULL; | |
| 139 delete sync; | |
| 140 } | |
| 141 | |
| 142 | |
| 143 TEST_CASE(MessageQueue_FlushAll) { | |
| 144 MessageQueue queue; | |
| 145 MessageQueueTestPeer queue_peer(&queue); | |
| 146 Dart_Port port1 = 1; | |
| 147 Dart_Port port2 = 2; | |
| 148 | |
| 149 // Add two messages. | |
| 150 PortMessage* msg1 = new PortMessage(port1, 0, strdup("msg1")); | |
| 151 queue.Enqueue(msg1); | |
| 152 PortMessage* msg2 = new PortMessage(port2, 0, strdup("msg2")); | |
| 153 queue.Enqueue(msg2); | |
| 154 | |
| 155 EXPECT(queue_peer.HasMessage()); | |
| 156 queue.FlushAll(); | |
| 157 EXPECT(!queue_peer.HasMessage()); | |
| 158 | |
| 159 // msg1 and msg2 already delete by FlushAll. | |
| 160 } | |
| 161 | |
| 162 | |
| 163 TEST_CASE(MessageQueue_Flush) { | |
| 164 MessageQueue queue; | |
| 165 MessageQueueTestPeer queue_peer(&queue); | |
| 166 Dart_Port port1 = 1; | |
| 167 Dart_Port port2 = 2; | |
| 168 | |
| 169 // Add two messages on different ports. | |
| 170 PortMessage* msg1 = new PortMessage(port1, 0, strdup("msg1")); | |
| 171 queue.Enqueue(msg1); | |
| 172 PortMessage* msg2 = new PortMessage(port2, 0, strdup("msg2")); | |
| 173 queue.Enqueue(msg2); | |
| 174 EXPECT(queue_peer.HasMessage()); | |
| 175 | |
| 176 queue.Flush(port1); | |
| 177 | |
| 178 // One message is left in the queue. | |
| 179 EXPECT(queue_peer.HasMessage()); | |
| 180 PortMessage* msg = queue.Dequeue(0); | |
| 102 EXPECT(msg != NULL); | 181 EXPECT(msg != NULL); |
| 103 intptr_t remote = GetIntData(msg->data()); // Get the remote port. | 182 EXPECT_STREQ("msg2", reinterpret_cast<char*>(msg->data())); |
| 104 delete msg; | 183 |
| 105 | 184 EXPECT(!queue_peer.HasMessage()); |
| 106 for (intptr_t i = 0; i < 10; i++) { | 185 |
| 107 PortMap::PostMessage(new PortMessage(remote, 0, AllocIntData(i))); | 186 // msg1 is already deleted by Flush. |
| 108 PortMessage* msg = PortMap::ReceiveMessage(0); | 187 delete msg2; |
| 109 EXPECT_EQ(local, msg->dest_id()); | 188 } |
| 110 EXPECT(msg != NULL); | 189 |
| 111 EXPECT_EQ(i * 2, GetIntData(msg->data())); | 190 |
| 112 delete msg; | 191 TEST_CASE(MessageQueue_Flush_MultipleMessages) { |
| 113 } | 192 MessageQueue queue; |
| 114 | 193 MessageQueueTestPeer queue_peer(&queue); |
| 115 PortMap::PostMessage(new PortMessage(remote, 0, AllocIntData(kEOT))); | 194 Dart_Port port1 = 1; |
| 116 msg = PortMap::ReceiveMessage(0); | 195 |
| 117 EXPECT_EQ(local, msg->dest_id()); | 196 PortMessage* msg1 = new PortMessage(port1, 0, strdup("msg1")); |
| 118 EXPECT(msg != NULL); | 197 queue.Enqueue(msg1); |
| 119 EXPECT_EQ(kEOT, GetIntData(msg->data())); | 198 PortMessage* msg2 = new PortMessage(port1, 0, strdup("msg2")); |
| 120 delete msg; | 199 queue.Enqueue(msg2); |
| 121 | 200 EXPECT(queue_peer.HasMessage()); |
| 122 // Give the spawned thread enough time to properly exit. | 201 |
| 123 Monitor* waiter = new Monitor(); | 202 queue.Flush(port1); |
| 124 { | 203 |
| 125 MonitorLocker ml(waiter); | 204 // Queue is empty. |
| 126 ml.Wait(20); | 205 EXPECT(!queue_peer.HasMessage()); |
| 127 } | 206 // msg1 and msg2 are already deleted by Flush. |
| 128 delete waiter; | 207 } |
| 129 } | 208 |
| 209 | |
| 210 TEST_CASE(MessageQueue_Flush_EmptyQueue) { | |
| 211 MessageQueue queue; | |
| 212 MessageQueueTestPeer queue_peer(&queue); | |
| 213 Dart_Port port1 = 1; | |
| 214 | |
| 215 EXPECT(!queue_peer.HasMessage()); | |
| 216 queue.Flush(port1); | |
| 217 | |
| 218 // Queue is still empty. | |
| 219 EXPECT(!queue_peer.HasMessage()); | |
| 220 } | |
|
siva
2011/10/14 23:50:32
I was thinking of having a port3 with 0 messages a
turnidge
2011/10/17 18:38:48
Ok. I like to keep my unit tests as small as I ca
| |
| 221 | |
| 130 | 222 |
| 131 } // namespace dart | 223 } // namespace dart |
| OLD | NEW |