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.

293 lines
8.7KB

  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 "JackPosixSemaphore.h"
  16. #include "JackTools.h"
  17. #include "JackConstants.h"
  18. #include "JackError.h"
  19. #include "JackPosixCommon.h"
  20. #include <fcntl.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <sys/time.h>
  24. #ifdef __linux__
  25. #include "promiscuous.h"
  26. #endif
  27. #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  28. #define JACK_SEM_PREFIX "/jack_sem"
  29. #define SEM_DEFAULT_O 0
  30. #else
  31. #define JACK_SEM_PREFIX "jack_sem"
  32. #define SEM_DEFAULT_O O_RDWR
  33. #endif
  34. namespace Jack
  35. {
  36. JackPosixSemaphore::JackPosixSemaphore() : JackSynchro(), fSemaphore(NULL)
  37. {
  38. const char* promiscuous = getenv("JACK_PROMISCUOUS_SERVER");
  39. fPromiscuous = (promiscuous != NULL);
  40. #ifdef __linux__
  41. fPromiscuousGid = jack_group2gid(promiscuous);
  42. #endif
  43. }
  44. void JackPosixSemaphore::BuildName(const char* client_name, const char* server_name, char* res, int size)
  45. {
  46. char ext_client_name[SYNC_MAX_NAME_SIZE + 1];
  47. JackTools::RewriteName(client_name, ext_client_name);
  48. #if __APPLE__ // POSIX semaphore names are limited to 32 characters...
  49. snprintf(res, 32, "js_%s", ext_client_name);
  50. #else
  51. if (fPromiscuous) {
  52. snprintf(res, size, JACK_SEM_PREFIX ".%s_%s", server_name, ext_client_name);
  53. } else {
  54. snprintf(res, size, JACK_SEM_PREFIX ".%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name);
  55. }
  56. #endif
  57. }
  58. bool JackPosixSemaphore::Signal()
  59. {
  60. int res;
  61. if (!fSemaphore) {
  62. jack_error("JackPosixSemaphore::Signal name = %s already deallocated!!", fName);
  63. return false;
  64. }
  65. if (fFlush) {
  66. return true;
  67. }
  68. if ((res = sem_post(fSemaphore)) != 0) {
  69. jack_error("JackPosixSemaphore::Signal name = %s err = %s", fName, strerror(errno));
  70. }
  71. return (res == 0);
  72. }
  73. bool JackPosixSemaphore::SignalAll()
  74. {
  75. int res;
  76. if (!fSemaphore) {
  77. jack_error("JackPosixSemaphore::SignalAll name = %s already deallocated!!", fName);
  78. return false;
  79. }
  80. if (fFlush) {
  81. return true;
  82. }
  83. if ((res = sem_post(fSemaphore)) != 0) {
  84. jack_error("JackPosixSemaphore::SignalAll name = %s err = %s", fName, strerror(errno));
  85. }
  86. return (res == 0);
  87. }
  88. bool JackPosixSemaphore::Wait()
  89. {
  90. int res;
  91. if (!fSemaphore) {
  92. jack_error("JackPosixSemaphore::Wait name = %s already deallocated!!", fName);
  93. return false;
  94. }
  95. while ((res = sem_wait(fSemaphore) < 0)) {
  96. jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno));
  97. if (errno != EINTR) {
  98. break;
  99. }
  100. }
  101. return (res == 0);
  102. }
  103. bool JackPosixSemaphore::TimedWait(long usec)
  104. {
  105. int res;
  106. struct timespec rel_timeout, now_mono, end_mono, now_real, end_real;
  107. struct timespec diff_mono;
  108. if (!fSemaphore) {
  109. jack_error("JackPosixSemaphore::TimedWait name = %s already deallocated!!", fName);
  110. return false;
  111. }
  112. /* Convert usec argument to timespec */
  113. rel_timeout.tv_sec = usec / 1000000;
  114. rel_timeout.tv_nsec = (usec % 1000000) * 1000;
  115. /* Calculate absolute monotonic timeout */
  116. res = clock_gettime(CLOCK_MONOTONIC, &now_mono);
  117. if (res != 0) {
  118. /* CLOCK_MONOTONIC is not supported, do not check for time skips. */
  119. res = clock_gettime(CLOCK_REALTIME, &now_real);
  120. assert(res == 0);
  121. JackPosixTools::TimespecAdd(&now_real, &rel_timeout, &end_real);
  122. while ((res = sem_timedwait(fSemaphore, &end_real)) < 0) {
  123. if (errno != EINTR) {
  124. break;
  125. }
  126. }
  127. if (res == 0) {
  128. return true;
  129. } else {
  130. goto err;
  131. }
  132. }
  133. JackPosixTools::TimespecAdd(&now_mono, &rel_timeout, &end_mono);
  134. /* sem_timedwait() is affected by abrupt time jumps, i.e. when the system
  135. * time is changed. To protect against this, measure the time difference
  136. * between and after the sem_timedwait() call and if it suggests that there
  137. * has been a time jump, restart the call. */
  138. for (;;) {
  139. /* Calculate absolute realtime timeout, assuming no steps */
  140. res = clock_gettime(CLOCK_REALTIME, &now_real);
  141. assert(res == 0);
  142. JackPosixTools::TimespecSub(&end_mono, &now_mono, &diff_mono);
  143. JackPosixTools::TimespecAdd(&now_real, &diff_mono, &end_real);
  144. while ((res = sem_timedwait(fSemaphore, &end_real)) < 0) {
  145. if (errno != EINTR) {
  146. break;
  147. }
  148. }
  149. if (res == 0) {
  150. return true;
  151. }
  152. if (errno != ETIMEDOUT) {
  153. goto err;
  154. }
  155. /* Compare with monotonic timeout, in case a step happened */
  156. int old_errno = errno;
  157. res = clock_gettime(CLOCK_MONOTONIC, &now_mono);
  158. assert(res == 0);
  159. errno = old_errno;
  160. if (JackPosixTools::TimespecCmp(&now_mono, &end_mono) >= 0) {
  161. goto err;
  162. }
  163. }
  164. return true;
  165. err:
  166. jack_error("JackPosixSemaphore::TimedWait err = %s", strerror(errno));
  167. jack_log("JackPosixSemaphore::TimedWait now : %ld %ld ", now_real.tv_sec,
  168. now_real.tv_nsec / 1000);
  169. jack_log("JackPosixSemaphore::TimedWait next : %ld %ld ", end_real.tv_sec,
  170. end_real.tv_nsec / 1000);
  171. return false;
  172. }
  173. // Server side : publish the semaphore in the global namespace
  174. bool JackPosixSemaphore::Allocate(const char* name, const char* server_name, int value)
  175. {
  176. BuildName(name, server_name, fName, sizeof(fName));
  177. jack_log("JackPosixSemaphore::Allocate name = %s val = %ld", fName, value);
  178. if ((fSemaphore = sem_open(fName, O_CREAT | SEM_DEFAULT_O, 0777, value)) == (sem_t*)SEM_FAILED) {
  179. jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno));
  180. return false;
  181. } else {
  182. #ifdef __linux__
  183. if (fPromiscuous) {
  184. char sempath[SYNC_MAX_NAME_SIZE+13];
  185. snprintf(sempath, sizeof(sempath), "/dev/shm/sem.%s", fName);
  186. if (jack_promiscuous_perms(-1, sempath, fPromiscuousGid) < 0)
  187. return false;
  188. }
  189. #endif
  190. return true;
  191. }
  192. }
  193. // Client side : get the published semaphore from server
  194. bool JackPosixSemaphore::ConnectInput(const char* name, const char* server_name)
  195. {
  196. BuildName(name, server_name, fName, sizeof(fName));
  197. jack_log("JackPosixSemaphore::Connect name = %s", fName);
  198. // Temporary...
  199. if (fSemaphore) {
  200. jack_log("Already connected name = %s", name);
  201. return true;
  202. }
  203. if ((fSemaphore = sem_open(fName, SEM_DEFAULT_O)) == (sem_t*)SEM_FAILED) {
  204. jack_error("Connect: can't connect named semaphore name = %s err = %s", fName, strerror(errno));
  205. return false;
  206. } else if (fSemaphore) {
  207. int val = 0;
  208. sem_getvalue(fSemaphore, &val);
  209. jack_log("JackPosixSemaphore::Connect sem_getvalue %ld", val);
  210. return true;
  211. } else {
  212. jack_error("Connect: fSemaphore not initialized!");
  213. return false;
  214. }
  215. }
  216. bool JackPosixSemaphore::Connect(const char* name, const char* server_name)
  217. {
  218. return ConnectInput(name, server_name);
  219. }
  220. bool JackPosixSemaphore::ConnectOutput(const char* name, const char* server_name)
  221. {
  222. return ConnectInput(name, server_name);
  223. }
  224. bool JackPosixSemaphore::Disconnect()
  225. {
  226. if (fSemaphore) {
  227. jack_log("JackPosixSemaphore::Disconnect name = %s", fName);
  228. if (sem_close(fSemaphore) != 0) {
  229. jack_error("Disconnect: can't disconnect named semaphore name = %s err = %s", fName, strerror(errno));
  230. return false;
  231. } else {
  232. fSemaphore = NULL;
  233. return true;
  234. }
  235. } else {
  236. return true;
  237. }
  238. }
  239. // Server side : destroy the semaphore
  240. void JackPosixSemaphore::Destroy()
  241. {
  242. if (fSemaphore != NULL) {
  243. jack_log("JackPosixSemaphore::Destroy name = %s", fName);
  244. sem_unlink(fName);
  245. if (sem_close(fSemaphore) != 0) {
  246. jack_error("Destroy: can't destroy semaphore name = %s err = %s", fName, strerror(errno));
  247. }
  248. fSemaphore = NULL;
  249. } else {
  250. jack_error("JackPosixSemaphore::Destroy semaphore == NULL");
  251. }
  252. }
  253. } // end of namespace