jack1 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.

467 lines
9.3KB

  1. /*
  2. Copyright (C) 2003 Paul Davis
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 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 General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. $Id$
  15. */
  16. #include <unistd.h>
  17. #include <fcntl.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <signal.h>
  21. #include <limits.h>
  22. #include <errno.h>
  23. #include <sys/mman.h>
  24. #include <sys/types.h>
  25. #include <dirent.h>
  26. #include <sys/ipc.h>
  27. #include <sys/shm.h>
  28. #include <config.h>
  29. #include <jack/shm.h>
  30. #include <jack/internal.h>
  31. static jack_shm_registry_t* jack_shm_registry;
  32. static void
  33. jack_shm_lock_registry ()
  34. {
  35. /* XXX magic with semaphores here */
  36. }
  37. static void
  38. jack_shm_unlock_registry ()
  39. {
  40. /* XXX magic with semaphores here */
  41. }
  42. jack_shm_registry_t *
  43. jack_get_free_shm_info ()
  44. {
  45. jack_shm_registry_t* si = NULL;
  46. int i;
  47. jack_shm_lock_registry ();
  48. for (i = 0; i < MAX_SHM_ID; ++i) {
  49. if (jack_shm_registry[i].size == 0) {
  50. break;
  51. }
  52. }
  53. if (i < MAX_SHM_ID) {
  54. si = &jack_shm_registry[i];
  55. }
  56. jack_shm_unlock_registry ();
  57. return si;
  58. }
  59. void
  60. jack_release_shm_info (jack_shm_registry_index_t index)
  61. {
  62. if (jack_shm_registry[index].allocator == getpid()) {
  63. jack_shm_lock_registry ();
  64. jack_shm_registry[index].size = 0;
  65. jack_shm_registry[index].allocator = 0;
  66. jack_shm_unlock_registry ();
  67. }
  68. }
  69. void
  70. jack_cleanup_shm ()
  71. {
  72. int i;
  73. int destroy;
  74. jack_shm_info_t copy;
  75. jack_initialize_shm ();
  76. jack_shm_lock_registry ();
  77. for (i = 0; i < MAX_SHM_ID; i++) {
  78. jack_shm_registry_t* r;
  79. r = &jack_shm_registry[i];
  80. copy.index = r->index;
  81. destroy = FALSE;
  82. if (r->allocator == getpid()) {
  83. /* allocated by this process, so unattach
  84. and destroy.
  85. */
  86. jack_release_shm (&copy);
  87. destroy = TRUE;
  88. } else {
  89. if (kill (r->allocator, 0)) {
  90. if (errno == ESRCH) {
  91. /* allocator no longer exists, so destroy */
  92. destroy = TRUE;
  93. }
  94. }
  95. }
  96. if (destroy) {
  97. jack_destroy_shm (&copy);
  98. r->size = 0;
  99. r->allocator = 0;
  100. }
  101. }
  102. jack_shm_unlock_registry ();
  103. }
  104. #if USE_POSIX_SHM
  105. int
  106. jack_initialize_shm ()
  107. {
  108. int shm_fd;
  109. jack_shmsize_t size;
  110. int new_registry = FALSE;
  111. int ret = -1;
  112. if (jack_shm_registry != NULL) {
  113. return 0;
  114. }
  115. /* grab a chunk of memory to store shm ids in. this is
  116. to allow clean up of all segments whenever JACK
  117. starts (or stops).
  118. */
  119. size = sizeof (jack_shm_registry_t) * MAX_SHM_ID;
  120. jack_shm_lock_registry ();
  121. /* try without O_CREAT to see if it already exists */
  122. if ((shm_fd = shm_open ("/jack-shm-registry", O_RDWR, 0666)) < 0) {
  123. if (errno == ENOENT) {
  124. /* it doesn't exist, so create it */
  125. if ((shm_fd = shm_open ("/jack-shm-registry", O_RDWR|O_CREAT, 0666)) < 0) {
  126. jack_error ("cannot create shm registry segment (%s)",
  127. strerror (errno));
  128. goto out;
  129. }
  130. new_registry = TRUE;
  131. } else {
  132. jack_error ("cannot open existing shm registry segment (%s)",
  133. strerror (errno));
  134. goto out;
  135. }
  136. }
  137. if (ftruncate (shm_fd, size) < 0) {
  138. jack_error ("cannot set size of engine shm registry "
  139. "(%s)", strerror (errno));
  140. goto out;
  141. }
  142. if ((jack_shm_registry = mmap (0, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
  143. jack_error ("cannot mmap shm registry segment (%s)",
  144. strerror (errno));
  145. goto out;
  146. }
  147. if (new_registry) {
  148. int i;
  149. memset (jack_shm_registry, 0, size);
  150. for (i = 0; i < MAX_SHM_ID; ++i) {
  151. jack_shm_registry[i].index = i;
  152. }
  153. fprintf (stderr, "JACK compiled with POSIX SHM support\n");
  154. }
  155. ret = 0;
  156. out:
  157. close (shm_fd);
  158. jack_shm_unlock_registry ();
  159. return ret;
  160. }
  161. void
  162. jack_destroy_shm (jack_shm_info_t* si)
  163. {
  164. shm_unlink (jack_shm_registry[si->index].id);
  165. jack_release_shm_info (si->index);
  166. }
  167. void
  168. jack_release_shm (jack_shm_info_t* si)
  169. {
  170. if (si->attached_at >= 0) {
  171. munmap (si->attached_at, jack_shm_registry[si->index].size);
  172. }
  173. }
  174. int
  175. jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si)
  176. {
  177. jack_shm_registry_t* registry;
  178. int shm_fd;
  179. if ((registry = jack_get_free_shm_info ()) == NULL) {
  180. return -1;
  181. }
  182. if ((shm_fd = shm_open (shm_name, O_RDWR|O_CREAT, 0666)) < 0) {
  183. jack_error ("cannot create shm segment %s (%s)", shm_name,
  184. strerror (errno));
  185. return -1;
  186. }
  187. if (ftruncate (shm_fd, size) < 0) {
  188. jack_error ("cannot set size of engine shm registry "
  189. "(%s)", strerror (errno));
  190. return -1;
  191. }
  192. close (shm_fd);
  193. registry->size = size;
  194. snprintf (registry->id, sizeof (registry->id), "%s", shm_name);
  195. registry->allocator = getpid();
  196. si->index = registry->index;
  197. return 0;
  198. }
  199. int
  200. jack_attach_shm (jack_shm_info_t* si)
  201. {
  202. int shm_fd;
  203. jack_shm_registry_t *registry = &jack_shm_registry[si->index];
  204. if ((shm_fd = shm_open (registry->id,
  205. O_RDWR, 0666)) < 0) {
  206. jack_error ("cannot open shm segment %s (%s)", registry->id,
  207. strerror (errno));
  208. return -1;
  209. }
  210. if ((si->attached_at = mmap (0, registry->size, PROT_READ|PROT_WRITE,
  211. MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
  212. jack_error ("cannot mmap shm segment %s (%s)",
  213. registry->id,
  214. strerror (errno));
  215. close (shm_fd);
  216. return -1;
  217. }
  218. close (shm_fd);
  219. return 0;
  220. }
  221. int
  222. jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size)
  223. {
  224. int shm_fd;
  225. jack_shm_registry_t *registry = &jack_shm_registry[si->index];
  226. if ((shm_fd = shm_open (registry->id, O_RDWR, 0666)) < 0) {
  227. jack_error ("cannot create shm segment %s (%s)", registry->id,
  228. strerror (errno));
  229. return -1;
  230. }
  231. munmap (si->attached_at, registry->size);
  232. if (ftruncate (shm_fd, size) < 0) {
  233. jack_error ("cannot set size of shm segment %s "
  234. "(%s)", registry->id, strerror (errno));
  235. return -1;
  236. }
  237. if ((si->attached_at = mmap (0, size, PROT_READ|PROT_WRITE,
  238. MAP_SHARED, shm_fd, 0))
  239. == MAP_FAILED) {
  240. jack_error ("cannot mmap shm segment %s (%s)", registry->id,
  241. strerror (errno));
  242. close (shm_fd);
  243. return -1;
  244. }
  245. close (shm_fd);
  246. return 0;
  247. }
  248. #else /* USE_POSIX_SHM */
  249. #define JACK_SHM_REGISTRY_KEY 0x282929
  250. int
  251. jack_initialize_shm ()
  252. {
  253. int shmflags;
  254. int shmid;
  255. key_t key;
  256. jack_shmsize_t size;
  257. int new_registry = FALSE;
  258. int ret = -1;
  259. if (jack_shm_registry != NULL) {
  260. return 0;
  261. }
  262. /* grab a chunk of memory to store shm ids in. this is
  263. to allow our parent to clean up all such ids when
  264. if we exit. otherwise, they can get lost in crash
  265. or debugger driven exits.
  266. */
  267. shmflags = 0666;
  268. key = JACK_SHM_REGISTRY_KEY;
  269. size = sizeof (jack_shm_registry_t) * MAX_SHM_ID;
  270. jack_shm_lock_registry ();
  271. /* try without IPC_CREAT to check if it already exists */
  272. if ((shmid = shmget (key, size, shmflags)) < 0) {
  273. if (errno == ENOENT) {
  274. if ((shmid = shmget (key, size, shmflags|IPC_CREAT)) < 0) {
  275. jack_error ("cannot create shm registry segment (%s)",
  276. strerror (errno));
  277. goto out;
  278. }
  279. new_registry = TRUE;
  280. } else {
  281. jack_error ("cannot use existing shm registry segment (%s)",
  282. strerror (errno));
  283. goto out;
  284. }
  285. }
  286. if ((jack_shm_registry = shmat (shmid, 0, 0)) < 0) {
  287. jack_error ("cannot attach shm registry segment (%s)",
  288. strerror (errno));
  289. goto out;
  290. }
  291. if (new_registry) {
  292. int i;
  293. memset (jack_shm_registry, 0, size);
  294. for (i = 0; i < MAX_SHM_ID; ++i) {
  295. jack_shm_registry[i].index = i;
  296. }
  297. fprintf (stderr, "JACK compiled with System V SHM support\n");
  298. }
  299. ret = 0;
  300. out:
  301. jack_shm_unlock_registry ();
  302. return ret;
  303. }
  304. void
  305. jack_destroy_shm (jack_shm_info_t* si)
  306. {
  307. shmctl (jack_shm_registry[si->index].id, IPC_RMID, NULL);
  308. jack_release_shm_info (si->index);
  309. }
  310. void
  311. jack_release_shm (jack_shm_info_t* si)
  312. {
  313. if (si->attached_at >= 0) {
  314. shmdt (si->attached_at);
  315. }
  316. }
  317. int
  318. jack_shmalloc (const char* name_not_used, jack_shmsize_t size, jack_shm_info_t* si)
  319. {
  320. int shmflags;
  321. int shmid;
  322. jack_shm_registry_t* registry;
  323. if ((registry = jack_get_free_shm_info ()) == NULL) {
  324. return -1;
  325. }
  326. shmflags = 0666 | IPC_CREAT | IPC_EXCL;
  327. if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) < 0) {
  328. jack_error ("cannot create shm segment %s (%s)",
  329. name_not_used, strerror (errno));
  330. return -1;
  331. }
  332. registry->size = size;
  333. registry->id = shmid;
  334. registry->allocator = getpid();
  335. si->index = registry->index;
  336. return 0;
  337. }
  338. int
  339. jack_attach_shm (jack_shm_info_t* si)
  340. {
  341. if ((si->attached_at = shmat (jack_shm_registry[si->index].id, 0, 0)) < 0) {
  342. jack_error ("cannot attach shm segment (%s)",
  343. strerror (errno));
  344. jack_release_shm_info (si->index);
  345. return -1;
  346. }
  347. return 0;
  348. }
  349. int
  350. jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size)
  351. {
  352. /* There is no way to resize a System V shm segment. So, we
  353. * delete it and allocate a new one. This is tricky, because
  354. * the old segment will not disappear until all the clients
  355. * have released it. We can only do what we can from here.
  356. */
  357. jack_release_shm (si);
  358. jack_destroy_shm (si);
  359. if (jack_shmalloc ("not used", size, si)) {
  360. return -1;
  361. }
  362. return jack_attach_shm (si);
  363. }
  364. #endif /* !USE_POSIX_SHM */