docs/ikteam: Delete most files.
[haiku.git] / docs / develop / midi / testing.html
blobe6d434708f5abb4767bd76861bcf5ea59474d876
1 <HTML>
2 <BODY>
4 <H1>Testing the Midi Kit</H1>
6 <P>Most of the OpenBeOS source code has unit tests in the current/src/tests
7 directory. I looked into building CppUnit tests for the midi2 kit, but decided
8 that it doesn't really make much sense. Unit tests work best if you can test
9 something in isolation, but in the case of the midi2 kit this is very hard to
10 achieve. Because the classes from libmidi2.so always need to talk to the
11 midi_server, the tests depend on too many external factors. The available
12 endpoints, for example, will differ from system to system. The spray and hook
13 functions are difficult to test this way, too.</P>
15 <P>So instead of a CppUnit test suite, here is a list of manual tests that I
16 performed when developing the midi2 kit:</P>
18 <HR SIZE="1">
20 <H2>Registering the application</H2>
22 <P><I>Required:</I> Client app that calls BMidiRoster::MidiRoster()</P>
24 <UL>
26 <LI><P>When a client app starts, it should first receive mNEW notifications for
27 all endpoints in the system (even unregistered remotes), followed by mCON
28 notifications for all connections in the system (even those between two
29 unregistered local endpoints from another app).</P></LI>
31 <LI><P>Send invalid Mapp message (without messenger). The midi_server ignores
32 the request, and the client app blocks forever.</P></LI>
34 <LI><P>Fake a delivery error for the mNEW notifications and the mAPP reply.
35 (Add a snooze() in the midi_server's OnRegisterApplication(). While it is
36 snoozing, Ctrl-C the client app. Now the server can't deliver the message and
37 will unregister the application again.)</P></LI>
39 <LI><P>Kill the server. Start the client app. It should realize that the server
40 is not running, and return from MidiRoster(); it does not block
41 forever.</P></LI>
43 <LI><P>Note: The server does not protect against sending two or more Mapp
44 messages; it will add a new app_t object to the roster and it will also send
45 out the mNEW and mCON notifications again.</P></LI>
47 <LI><P>Verify that when the client app quits, the BMidiRoster instance is
48 destroyed by the BMidiRosterKiller. The BMidiRosterLooper is also destroyed,
49 along with any endpoint objects from its list. We don't destroy endpoints with
50 a refcount > 0, but print a warning message on stderr instead.</P></LI>
52 <LI><P>When the app quits before it has created a BMidiRoster instance, the
53 BMidiRosterKiller should do nothing.</P></LI>
55 </UL>
57 <HR SIZE="1">
59 <H2>Creating endpoints</H2>
61 <P><I>Required:</I> Client app that creates a new BMidiLocalProducer and/or
62 BMidiLocalConsumer</P>
64 <UL>
66 <LI><P>Send invalid Mnew message (missing fields). The server will return an
67 error code.</P></LI>
69 <LI><P>Don't send reply from midi_server. The client receives a B_NO_REPLY
70 error.</P></LI>
72 <LI><P>If something goes wrong creating a new local endpoint, you still get a
73 new BMidiEndpoint object (but it is not added to BMidiRosterLooper's internal
74 list of endpoints). Verify that its ID() function returns 0, and IsValid()
75 returns false. Verify that you can Release() it without crashing into the
76 debugger (i.e. the reference count of the new object should be 1).</P></LI>
78 <LI><P>Snooze in midi_server's OnCreateEndpoint() before sending reply to
79 client to simulate heavy processor load. Client should timeout. When done
80 snoozing, server fails to deliver the reply because the client is no longer
81 listening, and it unregisters the app.</P></LI>
83 <LI><P>Note: if you kill the client app with Ctrl-C before the server has sent
84 its reply, SendReply() still returns okay, and the midi_server adds the
85 endpoint, even though the corresponding app is dead. There is not much we can
86 do to prevent that (but it is not really a big deal).</P></LI>
88 <LI><P>Start the test app from two different Terminals. Verify that the new
89 local endpoint of app1 is added to the BMidiRosterLooper's list of endpoints,
90 and that its "isLocal" flag is true. Verify that when you start the second app,
91 it immediately receives mNEW notifications for the first app's endpoints. It
92 should also create BMidiEndpoint proxy objects for these endpoints with
93 "isLocal" set to false, and add them its own list. Vice versa for the endpoints
94 that app2 creates. Verify that the "registered" field in the mNEW notification
95 is false, because newly created endpoints are not registered yet. The
96 "properties" field should contain an empty message.</P></LI>
98 <LI><P>Start server. Start client app. The app makes new endpoints and the
99 server adds them to the roster. Ctrl-C the app. Start client app again. The new
100 client first receives mNEW notifications for the old app's endpoints. When the
101 new app tries to create its own endpoints, the server realizes that the old app
102 is dead, and sends mDEL notifications for the now-defunct endpoints.</P></LI>
104 <LI><P>The test app should now create 2 endpoints. Let the midi_server snooze
105 during the second create message, so the app times out. The server now
106 unregisters the app and purges its first endpoint (which was successfully
107 created).</P></LI>
109 <LI><P>The test app should now create 3 endpoints. Let the midi_server snooze
110 during the second create message, so the app times out. (It also times out when
111 sending the create request for the 3rd endpoint, because the server is still
112 snoozing.) Because it cannot send a reply for the 2nd create message, the
113 server now unregisters the app and purges its first endpoint (which was
114 successfully created). Then it processes the create request for the 3rd
115 endpoint, but ignores it because the app is now no longer registered with the
116 server.</P></LI>
118 <LI><P>Purging endpoints. The test app should now create 2 endpoints. Let the
119 midi_server snooze during the _fourth_ create message. Run the server. Run the
120 test app. Run the test app again in a second Terminal. The server times out,
121 and unregisters the second app. The first app should receive an mDEL
122 notification. Repeat, but now the test app should make 3 endpoints and the
123 server fails on the _sixth_ endpoint. The first app now receives 2 mDEL
124 notifications.</P></LI>
126 <LI><P>You should be allowed to pass NULL into the BMidiLocalProducer and
127 BMidiLocalConsumer constructor.</P></LI>
129 <LI><P>Let the midi_server assign random IDs to new endpoints; the
130 BMidiRosterLooper should sort the endpoints by their IDs when it adds them to
131 its internal list.</P></LI>
133 </UL>
135 <HR SIZE="1">
137 <H2>Deleting endpoints</H2>
139 <P><I>Required:</I> client app that creates one or more endpoints and
140 Release()'s them</P>
142 <UL>
144 <LI><P>Verify that Acquire() increments the endpoint's refcount and Release()
145 decrements it. When you Release() a local endpoint so its refcount becomes
146 zero, the client sends an Mdel request to the server. When you Release() a
147 local endpoint too many times, your app jumps into the debugger.</P></LI>
149 <LI><P>Send an Mdel request with an invalid ID to the server. Examples of
150 invalid IDs: -1, 0, 1000 (or any other large number).</P></LI>
152 <LI><P>Start the test app from two different Terminals. Note that when one of
153 the apps Release()'s its endpoints, the other receives corresponding mDEL
154 notifications.</P></LI>
156 <LI><P>Snooze in midi_server's OnCreateEndpoint() before sending reply to
157 "create endpoint" request. The client will timeout and the server will
158 unregister the app. Now have the client Release() the endpoint. This sends a
159 "delete endpoint" request to the server, which ignores the request because the
160 app is no longer registered.</P></LI>
162 <LI><P>Override BMidiLocalProducer and BMidiLocalConsumer, and provide a public
163 destructor. Call "delete prod; delete cons;" from your code, instead of using
164 Release(). Your app should drop into the debugger.</P></LI>
166 <LI><P>Start the client app and let it make its endpoints. Kill the server.
167 Release() the endpoints. The server doesn't run, so the Mdel request never
168 arrives, but the BMidiEndpoint objects should be deleted regardless.</P></LI>
170 <LI><P>Start the test app from two different Terminals, and let them make their
171 endpoints. Quit the apps (using the Deskbar's "Quit Application" menu item).
172 Verify that both clean up and exit correctly. App1 removes its own endpoint
173 from the BMidiRosterLooper's list of endpoints and sends an 'mDEL' message to
174 the server, which passes it on to app2. In response, app2 removes the proxy
175 object from its own list and deletes it. Again, vice versa for the endpoint
176 from app2.</P></LI>
178 <LI><P>Start both apps again and wait until they have notified each other about
179 the endpoints. Ctrl-C app1, and restart it. Verify that app1 receives the
180 'mNEW' messages and creates proxies for these remote endpoints. Both apps
181 should receive an 'mDEL' message for app1's old endpoint (because the
182 midi_server realizes it no longer exists and purges it), and remove it from
183 their lists accordingly.</P></LI>
185 </UL>
187 <HR SIZE="1">
189 <H2>Changing attributes</H2>
191 <P><I>Required:</I> Client app that creates an endpoint and calls Register(),
192 Unregister(), SetName(), and SetLatency()</P>
194 <UL>
196 <LI><P>Send an Mchg request with an invalid ID to the server.</P></LI>
198 <LI><P>Register() a local endpoint that is already registered. This does not
199 send a message to the server and always returns B_OK. Likewise for
200 Unregister()ing a local endpoint that is not registered.</P></LI>
202 <LI><P>Register() or Unregister() a remote endpoint, or an invalid local
203 endpoint. That should immediately return an error code.</P></LI>
205 <LI><P>Verify that BMidiRoster::Register() does the same thing as
206 BMidiEndpoint::Register(). Also for BMidiRoster::Unregister() and
207 BMidiEndpoint::Unregister().</P></LI>
209 <LI><P>If you pass NULL into BMidiRoster::Register() or Unregister(), the
210 functions immediately return with an error code.</P></LI>
212 <LI><P>SetName() should ignore NULL names. When you call it on a remote
213 endpoint, SetName() should do nothing. SetName() does not send a message if the
214 new name is the same as the current name.</P></LI>
216 <LI><P>SetLatency() should ignore negative values. SetLatency() does not send a
217 message if the new latency is the same as the current latency. (Since
218 SetLatency() lives in BMidiLocalConsumer, you can never use it on remote
219 endpoints.)</P></LI>
221 <LI><P>Kill the server after making the new endpoint, and call Register(). The
222 client app should return an error code. Also for Unregister(), SetName(),
223 SetLatency(), and SetProperties().</P></LI>
225 <LI><P>Snooze in the midi_server's OnChangeEndpoint() before sending the reply
226 to the client. Both sides will flag an error. No mCHG notifications will be
227 sent. The server unregisters the app and purges its endpoints.</P></LI>
229 <LI><P>Verify that other apps will receive mCHG notifications when the test app
230 successfully calls Register(), Unregister(), SetName(), and SetLatency(), and
231 that they modify the corresponding BMidiEndpoint objects accordingly. Since
232 clients are never notified when they change their own endpoints, they should
233 ignore the notifications that concern local endpoints. Latency changes should
234 be ignored if the endpoint is not a consumer.</P></LI>
236 <LI><P>Send an Mchg request with only the "midi:id" field, so no "midi:name",
237 "midi:registered", "midi:latency", or "midi:properties". The server will still
238 notify the other apps, although they will obviously ignore the notification,
239 because it doesn't contain any useful data.</P></LI>
241 <LI><P>The Mchg request is overloaded to change several attributes. Verify that
242 changing one of these attributes, such as the latency, does not overwrite/wipe
243 out the others.</P></LI>
245 <LI><P>Start app1. Wait until it has created and registered its endpoint. Start
246 app2. During the initial handshake, app2 should receive an 'mNEW' message for
247 app1's endpoint. Verify that the "refistered" field in this message is already
248 true, and that this is passed on correctly to the new BMidiEndpoint proxy
249 object.</P></LI>
251 <LI><P>GetProperties() should return NULL if the message parameter is
252 NULL.</P></LI>
254 <LI><P>The properties of new endpoints are empty. Create a new endpoint and
255 call GetProperties(). The BMessage that you receive should contain no
256 fields.</P></LI>
258 <LI><P>SetProperties() should return NULL if the message parameter is NULL. It
259 should return an error code if the endpoint is remote or invalid. It should
260 work fine on local endpoints, registered or not. SetProperties() does not
261 compare the contents of the new BMessage to the old, so it will always send out
262 the change request.</P></LI>
264 <LI><P>If you Unregister() an endpoint that is connected, the connection should
265 not be broken.</P></LI>
267 </UL>
269 <HR SIZE="1">
271 <H2>Consulting the roster</H2>
273 <P><I>Required:</I> Client app that creates several endpoints, and registers
274 some of them (not all), and uses the BMidiRoster::FindEndpoint() etc functions
275 to examine the roster.</P>
277 <UL>
279 <LI><P>Verify that FindEndpoint() returns NULL if you pass it:</P>
281 <UL>
282 <LI>invalid ID (localOnly = false)</LI>
283 <LI>invalid ID (localOnly = true)</LI>
284 <LI>remote non-registered endpoint (localOnly = false)</LI>
285 <LI>remote non-registered endpoint (localOnly = true)</LI>
286 <LI>remote registered endpoint (localOnly = true)</LI>
287 </UL><BR>
289 <P>Verify that FindEndpoint() returns a valid BMidiEndpoint object if you pass
290 it:</P>
292 <UL>
293 <LI>local non-registered endpoint (localOnly = false)</LI>
294 <LI>local non-registered endpoint (localOnly = true)</LI>
295 <LI>local registered endpoint (localOnly = false)</LI>
296 <LI>local registered endpoint (localOnly = true)</LI>
297 <LI>remote registered endpoint (localOnly = false)</LI>
298 </UL><BR>
300 </LI>
302 <LI><P>Verify that FindConsumer() works just like FindEndpoint(), but that it
303 also returns NULL if the endpoint with the specified ID is not a consumer.
304 Likewise for FindProducer().</P></LI>
306 <LI><P>Verify that NextEndpoint() returns NULL if you pass it NULL. It also
307 returns NULL if no more endpoints exist. Otherwise, it returns a BMidiEndpoint
308 object, bumps the endpoint's reference count, and sets the "id" parameter to
309 the ID of the endpoint. NextEndpoint() should never return local endpoints
310 (registered or not), nor unregistered remote endpoints. Verify that negative
311 "id" values also work.</P></LI>
313 <LI><P>Verify that you can safely call the Find and Next functions without
314 having somehow initialized the BMidiRoster first (by making a new endpoint, for
315 example). The functions themselves should call MidiRoster() and do the
316 handshake with the server.</P></LI>
318 <LI><P>The Find and Next functions should bump the reference count of the
319 BMidiEndpoint object that they return. However, they should not (inadvertently)
320 modify the refcounts of any other endpoint objects.</P></LI>
322 <LI><P>Get a BMidiEndpoint proxy for a remote published endpoint. Release().
323 Now it should not be removed from the endpoint list or even be deleted, even
324 though its reference count dropped to zero.</P></LI>
326 <LI><P>Start app1. Start app2. App2 gets a BMidiEndpoint proxy for a remote
327 endpoint from app1. Ctrl-C app1. Start app1 again. Now app2 receives an mDEL
328 message for app1's old endpoint. Verify that the endpoint is removed from the
329 endpoint list, but not deleted because its reference count isn't zero. If app2
330 now Release()s the endpoint, the BMidiEndpoint object should be deleted. Try
331 again, but now Release() the endpoint before you Ctrl-C; now it should be
332 deleted and removed from the list when you start app1 again.</P></LI>
334 </UL>
336 <HR SIZE="1">
338 <H2>Making/breaking connections</H2>
340 <P><I>Required:</I> Client app that creates a producer and consumer endpoint,
341 optionally registers them, consults the roster for remote endpoints, and makes
342 various kinds of connections.</P>
344 <UL>
346 <LI><P>Test the following for BMidiProducer::Connect():</P>
348 <UL>
349 <LI>Connect(NULL)</LI>
350 <LI>Connect(invalid consumer)</LI>
351 <LI>Connect() using an invalid producer</LI>
352 <LI>Send Mcon request with invalid IDs</LI>
353 <LI>Kill the midi_server just before you Connect()</LI>
354 <LI>Let the midi_server snooze, so the connect request times out</LI>
355 <LI>Have the midi_server return an error result code</LI>
356 <LI>On successful connect, verify that the consumer is added to the producer's
357 list of endpoints</LI>
358 <LI>Verify that you can make connections between 2 local endpoints, a local
359 producer and a remote consumer, a remote producer and a local consumer, and two
360 2 remote endpoints. Test the local endpoints both registered and
361 unregistered.</LI>
362 <LI>2x Connect() on same consumer should give an error</LI>
363 <LI>The other applications should receive an mCON notification, and adjust
364 their own local rosters accordingly</LI>
365 <LI>If you are calling Connect() on a local producer, its Connected() hook
366 should be called. If you are calling Connect() on a remote producer, then its
367 own application should call the Connected() hook.</LI>
368 </UL><BR></LI>
370 <LI><P>Test the following for BMidiProducer::Disconnect():</P>
372 <UL>
373 <LI>Disconnect(NULL)</LI>
374 <LI>Disconnect(invalid consumer)</LI>
375 <LI>Disconnect() using an invalid producer</LI>
376 <LI>Send Mdis request with invalid IDs</LI>
377 <LI>Kill the midi_server just before you Disconnect()</LI>
378 <LI>Let the midi_server snooze, so the disconnect request times out</LI>
379 <LI>Have the midi_server return an error result code</LI>
380 <LI>On successful disconnect, verify that the consumer is removed from the
381 producer's list of endpoints</LI>
382 <LI>Verify that you can break connections between 2 local endpoints, a local
383 producer and a remote consumer, a remote producer and a local consumer, and two
384 2 remote endpoints. Test the local endpoints both registered and
385 unregistered.</LI>
386 <LI>Disconnecting 2 endpoints that were not connected should give an error</LI>
387 <LI>The other applications should receive an mDIS notification, and adjust
388 their own local rosters accordingly</LI>
389 <LI>If you are calling Disconnect() on a local producer, its Disconnected()
390 hook should be called. If you are calling Disconnect() on a remote producer,
391 then its own application should call the Disconnected() hook.</LI>
392 </UL><BR></LI>
394 <LI><P>Make a connection on a local producer. Release() the producer. The other
395 app should only receive an mDEL notification. Likewise if you have a connection
396 with a local consumer and you Release() that. However, now all apps should
397 throw away this consumer from the connection lists, invoking the Disconnected()
398 hook of local producers. The same thing happens if you Ctrl-C the app and
399 restart it. (Now the old endpoints are purged.)</P></LI>
401 <LI><P>BMidiProducer::IsConnected() should return false if you pass NULL or an
402 invalid consumer.</P></LI>
404 <LI><P>BMidiProducer::Connections() should return a new BList every time you
405 call it. The objects in this list are the BMidiConsumers that are connected to
406 this producer; verify that their reference counts are bumped for every call to
407 Connections().</P></LI>
409 </UL>
411 <HR SIZE="1">
413 <H2>Watching</H2>
415 <P><I>Required:</I> Client app that creates local consumer and producer
416 endpoints, and calls Register(), Unregister(), SetName(), SetLatency(), and
417 SetProperties(). It should also make and break connections.</P>
419 <UL>
421 <LI><P>When you call StartWatching(), you should receive B_MIDI_EVENT
422 notifications for all remote registered endpoints and the connections between
423 them. You will get no notifications for local endpoints, or for any connections
424 that involve unregistered endpoints. The BMidiRosterLooper should make a copy
425 of the BMessenger, so when the client destroys the original messenger, you will
426 still receive notifications. Verify that calling StartWatching() with the same
427 BMessenger twice in a row will also send the initial set of notifications
428 twice. StartWatching(NULL) should be ignored and does not remove the current
429 messenger.</P></LI>
431 <LI><P>Run the client app from two different Terminals. Verify that you receive
432 properly formatted B_MIDI_EVENT notifications when the other app changes the
433 attributes of its <I>registered</I> endpoints with the various Set() functions.
434 You should also receive notifications if the app Register()s or Unregister()s
435 its endpoints. That app that makes these changes does not receive the
436 notifications.</P></LI>
438 <LI><P>Run the client app from two different Terminals. Verify that you receive
439 properly formatted B_MIDI_EVENT notifications when the apps make and break
440 connections. Every app receives these connection notifications, whether the
441 endpoints are published or not. The app that makes and breaks the connections
442 does not receive any notifications.</P></LI>
444 <LI><P>StopWatching() should delete BMidiRosterLooper's BMessenger copy, if
445 any. Verify that you no longer receive B_MIDI_EVENT notifications for remote
446 endpoints after you have called StopWatching().</P></LI>
448 <LI><P>If the client is watching, and the BMidiRosterLooper receives an mDEL
449 notification for a registered remote endpoint, it should also send an
450 "unregistered" B_MIDI_EVENT to let the client know that this endpoint is no
451 longer available. If the endpoint was connected to anything, you'll also
452 receive "disconnected" B_MIDI_EVENTs.</P></LI>
454 <LI><P>If you get a "registered" event, and you do FindEndpoint() for that id,
455 you'll get its BMidiEndpoint object. If you get an "unregistered" event, then
456 FindEndpoint() returns NULL. So the events are send <I>after</I> the roster is
457 modified.</P></LI>
459 </UL>
461 <HR SIZE="1">
463 <H2>Event tests</H2>
465 <P><I>Required:</I> Several client apps that create and register consumer
466 endpoints that override the various MIDI event hook functions, as well as
467 producer endpoints that spray MIDI events. Also useful is a tool that lets you
468 make connections between all these endpoints (PatchBay), and a tool that lets
469 you monitor the MIDI events (MidiMonitor).</P>
471 <UL>
473 <LI><P>BMidiLocalProducer's spray functions should only try to send something
474 if there is one or more connected consumer. If the spray functions cannot
475 deliver their events, they simply ignore that consumer until the next spray.
476 (No connections are broken or anything.)</P></LI>
478 <LI><P>All spray functions except SprayData() should set the atomic flag to
479 true, even SpraySystemExclusive().</P></LI>
481 <LI><P>When you send a sysex message using SpraySystemExclusive(), it should
482 add 0xF0 in front of your data and 0xF7 at the back. When you call SprayData()
483 instead, no bytes are added to the MIDI event data.</P></LI>
485 <LI><P>Verify that all events arrive correctly and that the latency is minimal,
486 even when the load is heavy (i.e. many events are being sprayed to many
487 different consumers).</P></LI>
489 <LI><P>Verify that the BMidiLocalConsumer destructor properly destroys the
490 corresponding port and event thread before it returns.</P></LI>
492 <LI><P>BMidiLocalConsumer should ignore messages that are too small, addressed
493 to another consumer, or otherwise invalid.</P></LI>
495 <LI><P>BMidiLocalConsumer's Data() hook should ignore all non-atomic events.
496 The rest of the events, provided they contain the correct number of bytes for
497 that kind of event, are passed on to the other hooks.</P></LI>
499 <LI><P>Hook a producer up to a consumer and call all SprayXXX() functions with
500 a variety of arguments to make sure the correct hooks are being called with the
501 correct values. Call SprayData() and SpraySystemExclusive() with NULL data
502 and/or length 0.</P></LI>
504 <LI><P>Call GetProducerID() from one of BMidiLocalConsumer's hooks to verify
505 that this indeed returns the ID of the producer that sprayed the
506 event.</P></LI>
508 <LI><P>To test timeouts, first call SetTimeout(system_time() + 2000000), spray
509 an event to the consumer, and wait 2 seconds. The consumer's Timeout() hook
510 should now be called. Try again, but now spray multiple events to the consumer.
511 The Timeout() hook should still be called after 2 seconds, measured from the
512 moment the timeout was set. Replace the call to SetTimeout() with
513 SetTimeout(0). After spraying the first event, you should immediately get the
514 Timeout() signal, because the target time was set in the past. Verify that
515 calling SetTimeout() only takes effect after at least one new event has been
516 received.</P></LI>
518 </UL>
520 <HR SIZE="1">
522 <H2>Other tests</H2>
524 <UL>
526 <LI><P>Kill the server. Now run a client app. It should recognize that the
527 server isn't running, and return error codes on all operations. Also kill the
528 server while the test app is running. From then on, the client app will return
529 error codes on all operations. Also bring it back up again while the test app
530 is still running. Now the client app's request messages will be delivered to
531 the server again, but the server will ignore them, because our app did not
532 register with this new instance of the server.</P></LI>
534 <LI><P>Start the midi_server and several client apps. Use PatchBay to make and
535 break a whole bunch of connections. Quit PatchBay. Start it again. Now the same
536 connections should show up. Run similar tests with MidiKeyboard. Also install
537 VirtualMidi (and run the old midi_server for the time being) to get a whole
538 bunch of fake MIDI devices.</P></LI>
540 <LI><P><I>Regression bug:</I> After you quit one client app, another app fails
541 to send request to the midi_server.</P>
543 <P><I>Required:</I> Client app that creates a new endpoint and registers it. In
544 the app's destructor, it unregisters and releases the endpoint.</P>
546 <P><I>How to reproduce:</I> Run the app from two different Terminals. Ctrl-C
547 app1. Start app1 again. From the Deskbar quit both apps at the same time (that
548 is possible because app1 and app2 both have the same signature). When it tries
549 to send the Unregister() request to the midi_server, app2 gives the error
550 "Cannot send msg to server". The error code is "Bad Port ID", which means that
551 the reply port is dead. The Mdel message from Release() is sent without any
552 problems, however, because that expects no reply back. This is not the only way
553 to reproduce the problem, but it seems to be the most reliable one.</P>
555 <P>The reason this happens is because you kill app1. When app2 sends a
556 synchronous request to the midi_server, the server re-used that same message to
557 notify the other apps. (Because it already contained all the necessary fields.)
558 But app1 is dead, the notification fails, and this (probably) wipes out the
559 reply address in the message. I changed the midi_server to create new BMessages
560 for the notifications, and was no longer able to reproduce the
561 problem.</P></LI>
563 </UL>
565 </BODY>
566 </HTML>