jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

398 lines
13KB

  1. /*
  2. Copyright (C) 2004-2008 Grame
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2.1 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include "JackMachSemaphore.h"
  16. #include "JackMachUtils.h"
  17. #include "JackConstants.h"
  18. #include "JackTools.h"
  19. #include "JackError.h"
  20. #include <mach/message.h>
  21. #define jack_mach_error(kern_result, message) \
  22. jack_mach_error_uncurried("JackMachSemaphore", kern_result, message)
  23. #define jack_mach_bootstrap_err(kern_result, message, name) \
  24. jack_mach_bootstrap_err_uncurried("JackMachSemaphore", kern_result, message, name)
  25. namespace Jack
  26. {
  27. void JackMachSemaphore::BuildName(const char* client_name, const char* server_name, char* res, int size)
  28. {
  29. char ext_client_name[SYNC_MAX_NAME_SIZE + 1];
  30. JackTools::RewriteName(client_name, ext_client_name);
  31. // make the name as small as possible, as macos has issues with long semaphore names
  32. if (strcmp(server_name, "default") == 0)
  33. server_name = "";
  34. snprintf(res, std::min(size, 32), "js%d.%s%s", JackTools::GetUID(), server_name, ext_client_name);
  35. }
  36. bool JackMachSemaphore::Signal()
  37. {
  38. if (fSemaphore == MACH_PORT_NULL) {
  39. jack_error("JackMachSemaphore::Signal name = %s already deallocated!!", fName);
  40. return false;
  41. }
  42. if (fFlush) {
  43. return true;
  44. }
  45. kern_return_t res;
  46. if ((res = semaphore_signal(fSemaphore)) != KERN_SUCCESS) {
  47. jack_error("JackMachSemaphore::Signal name = %s err = %s", fName, mach_error_string(res));
  48. }
  49. return (res == KERN_SUCCESS);
  50. }
  51. bool JackMachSemaphore::SignalAll()
  52. {
  53. if (fSemaphore == MACH_PORT_NULL) {
  54. jack_error("JackMachSemaphore::SignalAll name = %s already deallocated!!", fName);
  55. return false;
  56. }
  57. if (fFlush) {
  58. return true;
  59. }
  60. kern_return_t res;
  61. // When signaled several times, do not accumulate signals...
  62. if ((res = semaphore_signal_all(fSemaphore)) != KERN_SUCCESS) {
  63. jack_error("JackMachSemaphore::SignalAll name = %s err = %s", fName, mach_error_string(res));
  64. }
  65. return (res == KERN_SUCCESS);
  66. }
  67. bool JackMachSemaphore::Wait()
  68. {
  69. if (fSemaphore == MACH_PORT_NULL) {
  70. jack_error("JackMachSemaphore::Wait name = %s already deallocated!!", fName);
  71. return false;
  72. }
  73. kern_return_t res;
  74. if ((res = semaphore_wait(fSemaphore)) != KERN_SUCCESS) {
  75. jack_error("JackMachSemaphore::Wait name = %s err = %s", fName, mach_error_string(res));
  76. }
  77. return (res == KERN_SUCCESS);
  78. }
  79. bool JackMachSemaphore::TimedWait(long usec)
  80. {
  81. if (fSemaphore == MACH_PORT_NULL) {
  82. jack_error("JackMachSemaphore::TimedWait name = %s already deallocated!!", fName);
  83. return false;
  84. }
  85. kern_return_t res;
  86. mach_timespec time;
  87. time.tv_sec = usec / 1000000;
  88. time.tv_nsec = (usec % 1000000) * 1000;
  89. if ((res = semaphore_timedwait(fSemaphore, time)) != KERN_SUCCESS) {
  90. jack_error("JackMachSemaphore::TimedWait name = %s usec = %ld err = %s", fName, usec, mach_error_string(res));
  91. }
  92. return (res == KERN_SUCCESS);
  93. }
  94. /*! \brief Server side: create semaphore and publish IPC primitives to make it accessible.
  95. *
  96. * This method;
  97. * - Allocates a mach semaphore
  98. * - Allocates a new mach IPC port and obtains a send right for it
  99. * - Publishes IPC port send right to the bootstrap server
  100. * - Starts a new JackMachSemaphoreServer thread, which listens for messages on the IPC port and
  101. * replies with a send right to the mach semaphore.
  102. *
  103. * \returns false if any of the above steps fails, or true otherwise.
  104. */
  105. bool JackMachSemaphore::Allocate(const char* client_name, const char* server_name, int value)
  106. {
  107. if (fSemaphore != MACH_PORT_NULL) {
  108. jack_error("JackMachSemaphore::Allocate: Semaphore already allocated; called twice? [%s]", fName);
  109. return false;
  110. }
  111. BuildName(client_name, server_name, fName, sizeof(fName));
  112. mach_port_t task = mach_task_self();
  113. kern_return_t res;
  114. if (fBootPort == MACH_PORT_NULL) {
  115. if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) {
  116. jack_mach_error(res, "can't find bootstrap mach port");
  117. return false;
  118. }
  119. }
  120. if ((res = semaphore_create(task, &fSemaphore, SYNC_POLICY_FIFO, value)) != KERN_SUCCESS) {
  121. jack_mach_error(res, "failed to create semaphore");
  122. return false;
  123. }
  124. if ((res = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &fServicePort)) != KERN_SUCCESS) {
  125. jack_mach_error(res, "failed to allocate IPC port");
  126. // Cleanup created semaphore
  127. this->Destroy();
  128. return false;
  129. }
  130. if ((res = mach_port_insert_right(mach_task_self(), fServicePort, fServicePort, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
  131. jack_mach_error(res, "failed to obtain send right for IPC port");
  132. // Cleanup created semaphore & mach port
  133. this->Destroy();
  134. return false;
  135. }
  136. if ((res = bootstrap_register(fBootPort, fName, fServicePort)) != KERN_SUCCESS) {
  137. jack_mach_bootstrap_err(res, "can't register IPC port with bootstrap server", fName);
  138. // Cleanup created semaphore & mach port
  139. this->Destroy();
  140. return false;
  141. }
  142. fSemServer = new JackMachSemaphoreServer(fSemaphore, fServicePort, fName);
  143. fThreadSemServer = new JackMachThread(fSemServer);
  144. if (fThreadSemServer->Start() < 0) {
  145. jack_error("JackMachSemaphore::Allocate: failed to start semaphore IPC server thread [%s]", fName);
  146. // Cleanup created semaphore, mach port (incl. service registration), and server
  147. this->Destroy();
  148. return false;
  149. }
  150. jack_log("JackMachSemaphore::Allocate: OK, name = %s", fName);
  151. return true;
  152. }
  153. /*! \brief Client side: Obtain semaphore from server via published IPC port.
  154. *
  155. * This method;
  156. * - Looks up the service port for the jackd semaphore server for this client by name
  157. * - Sends a message to that server asking for a semaphore port send right
  158. * - Receives a semaphore send right in return and stores it locally
  159. *
  160. * \returns False if any of the above steps fails, or true otherwise.
  161. */
  162. bool JackMachSemaphore::ConnectInput(const char* client_name, const char* server_name)
  163. {
  164. BuildName(client_name, server_name, fName, sizeof(fName));
  165. mach_port_t task = mach_task_self();
  166. kern_return_t res;
  167. if (fSemaphore != MACH_PORT_NULL) {
  168. jack_log("JackMachSemaphore::Connect: Already connected name = %s", fName);
  169. return true;
  170. }
  171. if (fBootPort == MACH_PORT_NULL) {
  172. if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) {
  173. jack_mach_error(res, "can't find bootstrap port");
  174. return false;
  175. }
  176. }
  177. if ((res = bootstrap_look_up(fBootPort, fName, &fServicePort)) != KERN_SUCCESS) {
  178. jack_mach_bootstrap_err(res, "can't find IPC service port to request semaphore", fName);
  179. return false;
  180. }
  181. mach_port_t semaphore_req_port;
  182. if ((res = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &semaphore_req_port)) != KERN_SUCCESS) {
  183. jack_mach_error(res, "failed to allocate request port");
  184. if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
  185. jack_mach_error(res, "failed to deallocate IPC service port during cleanup");
  186. } else {
  187. fServicePort = MACH_PORT_NULL;
  188. }
  189. return false;
  190. }
  191. // Prepare a message buffer on the stack. We'll use it for both sending and receiving a message.
  192. struct {
  193. mach_msg_header_t hdr;
  194. mach_msg_trailer_t trailer;
  195. } msg;
  196. /*
  197. * Configure the message to consume the destination port we give it (_MOVE_SEND), and to
  198. * transmute the local port receive right we give it into a send_once right at the destination.
  199. * The server will use that send_once right to reply to us.
  200. */
  201. msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
  202. msg.hdr.msgh_local_port = semaphore_req_port;
  203. msg.hdr.msgh_remote_port = fServicePort;
  204. mach_msg_return_t send_err = mach_msg(
  205. &msg.hdr,
  206. MACH_SEND_MSG,
  207. sizeof(msg.hdr), // no trailer on send
  208. 0,
  209. MACH_PORT_NULL,
  210. MACH_MSG_TIMEOUT_NONE,
  211. MACH_PORT_NULL);
  212. if (send_err != MACH_MSG_SUCCESS) {
  213. jack_mach_error(send_err, "failed to send semaphore port request IPC");
  214. if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
  215. jack_mach_error(res, "failed to deallocate IPC service port during cleanup");
  216. } else {
  217. fServicePort = MACH_PORT_NULL;
  218. }
  219. if ((res = mach_port_destroy(task, semaphore_req_port)) != KERN_SUCCESS) {
  220. jack_mach_error(res, "failed to destroy IPC request port during cleanup");
  221. }
  222. return false;
  223. } else {
  224. fServicePort = MACH_PORT_NULL; // We moved it into the message and away to the destination
  225. }
  226. mach_msg_return_t recv_err = mach_msg(
  227. &msg.hdr,
  228. MACH_RCV_MSG,
  229. 0,
  230. sizeof(msg),
  231. semaphore_req_port,
  232. MACH_MSG_TIMEOUT_NONE,
  233. MACH_PORT_NULL
  234. );
  235. /* Don't leak ports: irrespective of if we succeeded to read or not, destroy the port we created
  236. * to send/receive the request as we have no further use for it either way. */
  237. if ((res = mach_port_destroy(task, semaphore_req_port)) != KERN_SUCCESS) {
  238. jack_mach_error(res, "failed to destroy semaphore_req_port");
  239. // This isn't good, but doesn't actually stop the semaphore from working... don't bail
  240. }
  241. if (recv_err != MACH_MSG_SUCCESS) {
  242. jack_mach_error(recv_err, "failed to receive semaphore port");
  243. return false;
  244. } else {
  245. fSemaphore = msg.hdr.msgh_remote_port;
  246. jack_log("JackMachSemaphore::Connect: OK, name = %s ", fName);
  247. return true;
  248. }
  249. }
  250. bool JackMachSemaphore::Connect(const char* name, const char* server_name)
  251. {
  252. return ConnectInput(name, server_name);
  253. }
  254. bool JackMachSemaphore::ConnectOutput(const char* name, const char* server_name)
  255. {
  256. return ConnectInput(name, server_name);
  257. }
  258. bool JackMachSemaphore::Disconnect()
  259. {
  260. if (fSemaphore == MACH_PORT_NULL) {
  261. return true;
  262. }
  263. mach_port_t task = mach_task_self();
  264. kern_return_t res;
  265. jack_log("JackMachSemaphore::Disconnect name = %s", fName);
  266. if (fServicePort != MACH_PORT_NULL) {
  267. // If we're still holding onto a service port send right for some reason, deallocate it
  268. if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
  269. jack_mach_error(res, "failed to deallocate stray service port");
  270. // Continue cleanup even if this fails; don't bail
  271. } else {
  272. fServicePort = MACH_PORT_NULL;
  273. }
  274. }
  275. if ((res = mach_port_deallocate(task, fSemaphore)) != KERN_SUCCESS) {
  276. jack_mach_error(res, "failed to deallocate semaphore port");
  277. return false;
  278. } else {
  279. fSemaphore = MACH_PORT_NULL;
  280. return true;
  281. }
  282. }
  283. // Server side : destroy the JackGlobals
  284. void JackMachSemaphore::Destroy()
  285. {
  286. kern_return_t res;
  287. mach_port_t task = mach_task_self();
  288. if (fSemaphore == MACH_PORT_NULL) {
  289. jack_error("JackMachSemaphore::Destroy semaphore is MACH_PORT_NULL; already destroyed?");
  290. return;
  291. }
  292. if (fThreadSemServer) {
  293. if (fThreadSemServer->Kill() < 0) {
  294. jack_error("JackMachSemaphore::Destroy failed to kill semaphore server thread...");
  295. // Oh dear. How sad. Never mind.
  296. }
  297. JackMachThread* thread = fThreadSemServer;
  298. fThreadSemServer = NULL;
  299. delete thread;
  300. }
  301. if (fSemServer) {
  302. JackMachSemaphoreServer* server = fSemServer;
  303. fSemServer = NULL;
  304. delete server;
  305. }
  306. if ((res = mach_port_destroy(task, fServicePort)) != KERN_SUCCESS) {
  307. jack_mach_error(res, "failed to destroy IPC port");
  308. } else {
  309. fServicePort = MACH_PORT_NULL;
  310. }
  311. if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) {
  312. jack_mach_error(res, "failed to destroy semaphore");
  313. } else {
  314. fSemaphore = MACH_PORT_NULL;
  315. }
  316. jack_log("JackMachSemaphore::Destroy: OK, name = %s", fName);
  317. }
  318. } // end of namespace