Remove non-jackdbus man pages
[jackdbus.git] / macosx / JackMachSemaphore.mm
blobc28fe5ab0d8162b3853152fb031e7caa28ac3fea
1 /*
2 Copyright (C) 2004-2008 Grame
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include "JackMachSemaphore.h"
21 #include "JackMachUtils.h"
22 #include "JackConstants.h"
23 #include "JackTools.h"
24 #include "JackError.h"
26 #include <mach/message.h>
28 #define jack_mach_error(kern_result, message) \
29         jack_mach_error_uncurried("JackMachSemaphore", kern_result, message)
31 #define jack_mach_bootstrap_err(kern_result, message, name) \
32         jack_mach_bootstrap_err_uncurried("JackMachSemaphore", kern_result, message, name)
34 namespace Jack
37 void JackMachSemaphore::BuildName(const char* client_name, const char* server_name, char* res, int size)
39     char ext_client_name[SYNC_MAX_NAME_SIZE + 1];
40     JackTools::RewriteName(client_name, ext_client_name);
42     // make the name as small as possible, as macos has issues with long semaphore names
43     if (strcmp(server_name, "default") == 0)
44         server_name = "";
46     snprintf(res, std::min(size, 32), "js%d.%s%s", JackTools::GetUID(), server_name, ext_client_name);
49 bool JackMachSemaphore::Signal()
51     if (fSemaphore == MACH_PORT_NULL) {
52         jack_error("JackMachSemaphore::Signal name = %s already deallocated!!", fName);
53         return false;
54     }
56     if (fFlush) {
57         return true;
58     }
60     kern_return_t res;
61     if ((res = semaphore_signal(fSemaphore)) != KERN_SUCCESS) {
62         jack_error("JackMachSemaphore::Signal name = %s err = %s", fName, mach_error_string(res));
63     }
64     return (res == KERN_SUCCESS);
67 bool JackMachSemaphore::SignalAll()
69     if (fSemaphore == MACH_PORT_NULL) {
70         jack_error("JackMachSemaphore::SignalAll name = %s already deallocated!!", fName);
71         return false;
72     }
74     if (fFlush) {
75         return true;
76     }
78     kern_return_t res;
79     // When signaled several times, do not accumulate signals...
80     if ((res = semaphore_signal_all(fSemaphore)) != KERN_SUCCESS) {
81         jack_error("JackMachSemaphore::SignalAll name = %s err = %s", fName, mach_error_string(res));
82     }
83     return (res == KERN_SUCCESS);
86 bool JackMachSemaphore::Wait()
88     if (fSemaphore == MACH_PORT_NULL) {
89         jack_error("JackMachSemaphore::Wait name = %s already deallocated!!", fName);
90         return false;
91     }
93     kern_return_t res;
94     if ((res = semaphore_wait(fSemaphore)) != KERN_SUCCESS) {
95         jack_error("JackMachSemaphore::Wait name = %s err = %s", fName, mach_error_string(res));
96     }
97     return (res == KERN_SUCCESS);
100 bool JackMachSemaphore::TimedWait(long usec)
102     if (fSemaphore == MACH_PORT_NULL) {
103         jack_error("JackMachSemaphore::TimedWait name = %s already deallocated!!", fName);
104         return false;
105     }
107     kern_return_t res;
108     mach_timespec time;
109     time.tv_sec = usec / 1000000;
110     time.tv_nsec = (usec % 1000000) * 1000;
112     if ((res = semaphore_timedwait(fSemaphore, time)) != KERN_SUCCESS) {
113         jack_error("JackMachSemaphore::TimedWait name = %s usec = %ld err = %s", fName, usec, mach_error_string(res));
114     }
115     return (res == KERN_SUCCESS);
118 /*! \brief Server side: create semaphore and publish IPC primitives to make it accessible.
120  * This method;
121  * - Allocates a mach semaphore
122  * - Allocates a new mach IPC port and obtains a send right for it
123  * - Publishes IPC port send right to the bootstrap server
124  * - Starts a new JackMachSemaphoreServer thread, which listens for messages on the IPC port and
125  * replies with a send right to the mach semaphore.
127  * \returns false if any of the above steps fails, or true otherwise.
128  */
129 bool JackMachSemaphore::Allocate(const char* client_name, const char* server_name, int value)
131     if (fSemaphore != MACH_PORT_NULL) {
132         jack_error("JackMachSemaphore::Allocate: Semaphore already allocated; called twice? [%s]", fName);
133         return false;
134     }
136     BuildName(client_name, server_name, fName, sizeof(fName));
138     mach_port_t task = mach_task_self();
139     kern_return_t res;
141     if (fBootPort == MACH_PORT_NULL) {
142         if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) {
143             jack_mach_error(res, "can't find bootstrap mach port");
144             return false;
145         }
146     }
148     if ((res = semaphore_create(task, &fSemaphore, SYNC_POLICY_FIFO, value)) != KERN_SUCCESS) {
149         jack_mach_error(res, "failed to create semaphore");
150         return false;
151     }
153     if ((res = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &fServicePort)) != KERN_SUCCESS) {
154         jack_mach_error(res, "failed to allocate IPC port");
156         // Cleanup created semaphore
157         this->Destroy();
159         return false;
160     }
162     if ((res = mach_port_insert_right(mach_task_self(), fServicePort, fServicePort, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
163         jack_mach_error(res, "failed to obtain send right for IPC port");
165         // Cleanup created semaphore & mach port
166         this->Destroy();
168         return false;
169     }
171     if ((res = bootstrap_register(fBootPort, fName, fServicePort)) != KERN_SUCCESS) {
172         jack_mach_bootstrap_err(res, "can't register IPC port with bootstrap server", fName);
174         // Cleanup created semaphore & mach port
175         this->Destroy();
177         return false;
178     }
180     fSemServer = new JackMachSemaphoreServer(fSemaphore, fServicePort, fName);
181     fThreadSemServer = new JackMachThread(fSemServer);
183     if (fThreadSemServer->Start() < 0) {
184         jack_error("JackMachSemaphore::Allocate: failed to start semaphore IPC server thread [%s]", fName);
186         // Cleanup created semaphore, mach port (incl. service registration), and server
187         this->Destroy();
189         return false;
190     }
192     jack_log("JackMachSemaphore::Allocate: OK, name = %s", fName);
193     return true;
196 /*! \brief Client side: Obtain semaphore from server via published IPC port.
198  * This method;
199  * - Looks up the service port for the jackd semaphore server for this client by name
200  * - Sends a message to that server asking for a semaphore port send right
201  * - Receives a semaphore send right in return and stores it locally
203  * \returns False if any of the above steps fails, or true otherwise.
204  */
205 bool JackMachSemaphore::ConnectInput(const char* client_name, const char* server_name)
207     BuildName(client_name, server_name, fName, sizeof(fName));
209     mach_port_t task = mach_task_self();
210     kern_return_t res;
212     if (fSemaphore != MACH_PORT_NULL) {
213         jack_log("JackMachSemaphore::Connect: Already connected name = %s", fName);
214         return true;
215     }
217     if (fBootPort == MACH_PORT_NULL) {
218         if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) {
219             jack_mach_error(res, "can't find bootstrap port");
220             return false;
221         }
222     }
224     if ((res = bootstrap_look_up(fBootPort, fName, &fServicePort)) != KERN_SUCCESS) {
225         jack_mach_bootstrap_err(res, "can't find IPC service port to request semaphore", fName);
226         return false;
227     }
229     mach_port_t semaphore_req_port;
231     if ((res = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &semaphore_req_port)) != KERN_SUCCESS) {
232         jack_mach_error(res, "failed to allocate request port");
234         if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
235             jack_mach_error(res, "failed to deallocate IPC service port during cleanup");
236         } else {
237             fServicePort = MACH_PORT_NULL;
238         }
240         return false;
241     }
243     // Prepare a message buffer on the stack. We'll use it for both sending and receiving a message.
244     struct {
245         mach_msg_header_t hdr;
246         mach_msg_trailer_t trailer;
247     } msg;
249     /*
250      * Configure the message to consume the destination port we give it (_MOVE_SEND), and to
251      * transmute the local port receive right we give it into a send_once right at the destination.
252      * The server will use that send_once right to reply to us.
253      */
254     msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
255     msg.hdr.msgh_local_port = semaphore_req_port;
256     msg.hdr.msgh_remote_port = fServicePort;
258     mach_msg_return_t send_err = mach_msg(
259         &msg.hdr,
260         MACH_SEND_MSG,
261         sizeof(msg.hdr), // no trailer on send
262         0,
263         MACH_PORT_NULL,
264         MACH_MSG_TIMEOUT_NONE,
265         MACH_PORT_NULL);
267     if (send_err != MACH_MSG_SUCCESS) {
268         jack_mach_error(send_err, "failed to send semaphore port request IPC");
270         if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
271             jack_mach_error(res, "failed to deallocate IPC service port during cleanup");
272         } else {
273             fServicePort = MACH_PORT_NULL;
274         }
276         if ((res = mach_port_destroy(task, semaphore_req_port)) != KERN_SUCCESS) {
277             jack_mach_error(res, "failed to destroy IPC request port during cleanup");
278         }
280         return false;
281     } else {
282         fServicePort = MACH_PORT_NULL; // We moved it into the message and away to the destination
283     }
285     mach_msg_return_t recv_err = mach_msg(
286         &msg.hdr,
287         MACH_RCV_MSG,
288         0,
289         sizeof(msg),
290         semaphore_req_port,
291         MACH_MSG_TIMEOUT_NONE,
292         MACH_PORT_NULL
293     );
295     /* Don't leak ports: irrespective of if we succeeded to read or not, destroy the port we created
296      * to send/receive the request as we have no further use for it either way. */
297     if ((res = mach_port_destroy(task, semaphore_req_port)) != KERN_SUCCESS) {
298         jack_mach_error(res, "failed to destroy semaphore_req_port");
299         // This isn't good, but doesn't actually stop the semaphore from working... don't bail
300     }
302     if (recv_err != MACH_MSG_SUCCESS) {
303         jack_mach_error(recv_err, "failed to receive semaphore port");
304         return false;
305     } else {
306         fSemaphore = msg.hdr.msgh_remote_port;
308         jack_log("JackMachSemaphore::Connect: OK, name = %s", fName);
309         return true;
310     }
313 bool JackMachSemaphore::Connect(const char* name, const char* server_name)
315     return ConnectInput(name, server_name);
318 bool JackMachSemaphore::ConnectOutput(const char* name, const char* server_name)
320     return ConnectInput(name, server_name);
323 bool JackMachSemaphore::Disconnect()
325     if (fSemaphore == MACH_PORT_NULL) {
326         return true;
327     }
329     mach_port_t task = mach_task_self();
330     kern_return_t res;
332     jack_log("JackMachSemaphore::Disconnect name = %s", fName);
334     if (fServicePort != MACH_PORT_NULL) {
335         // If we're still holding onto a service port send right for some reason, deallocate it
336         if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
337             jack_mach_error(res, "failed to deallocate stray service port");
338             // Continue cleanup even if this fails; don't bail
339         } else {
340             fServicePort = MACH_PORT_NULL;
341         }
342     }
344     if ((res = mach_port_deallocate(task, fSemaphore)) != KERN_SUCCESS) {
345         jack_mach_error(res, "failed to deallocate semaphore port");
346         return false;
347     } else {
348         fSemaphore = MACH_PORT_NULL;
349         return true;
350     }
353 // Server side : destroy the JackGlobals
354 void JackMachSemaphore::Destroy()
356     const mach_port_t task = mach_task_self();
357     kern_return_t res;
359     if (fSemaphore == MACH_PORT_NULL) {
360         jack_error("JackMachSemaphore::Destroy semaphore is MACH_PORT_NULL; already destroyed?");
361         return;
362     }
364     if (fSemServer && fSemServer->Invalidate()) {
365         fServicePort = MACH_PORT_NULL;
366         fSemaphore = MACH_PORT_NULL;
367     }
369     if (fThreadSemServer) {
370         if (fThreadSemServer->Stop() < 0) {
371             jack_error("JackMachSemaphore::Destroy failed to stop semaphore server thread...");
372             // Oh dear. How sad. Never mind.
373         }
375         JackMachThread* thread = fThreadSemServer;
376         fThreadSemServer = NULL;
377         delete thread;
378     }
380     if (fSemServer) {
381         JackMachSemaphoreServer* server = fSemServer;
382         fSemServer = NULL;
383         delete server;
384     }
386     if (fServicePort != MACH_PORT_NULL) {
387         if ((res = mach_port_destroy(task, fServicePort)) != KERN_SUCCESS) {
388             jack_mach_error(res, "failed to destroy IPC port");
389         } else {
390             fServicePort = MACH_PORT_NULL;
391         }
392     }
394     if (fSemaphore != MACH_PORT_NULL) {
395         if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) {
396             jack_mach_error(res, "failed to destroy semaphore");
397         } else {
398             fSemaphore = MACH_PORT_NULL;
399         }
400     }
402     jack_log("JackMachSemaphore::Destroy: OK, name = %s", fName);
405 } // end of namespace