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.

424 lines
13KB

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