summaryrefslogtreecommitdiff
path: root/libjack/shm.c
diff options
context:
space:
mode:
authorjoq <joq@0c269be4-1314-0410-8aa9-9f06e86f4224>2004-12-11 23:31:27 +0000
committerjoq <joq@0c269be4-1314-0410-8aa9-9f06e86f4224>2004-12-11 23:31:27 +0000
commit4ad5ffd031cf9177af7ca559e760314f1c7e327e (patch)
tree730af5dddfb77a90de90dc4f2c624cb0566aab1b /libjack/shm.c
parent4e1c06db7aa2a9717adffe84152d7bddc2d1fd71 (diff)
downloadjack1-4ad5ffd031cf9177af7ca559e760314f1c7e327e.tar.gz
[0.99.31] SHM registry locking
git-svn-id: svn+ssh://jackaudio.org/trunk/jack@839 0c269be4-1314-0410-8aa9-9f06e86f4224
Diffstat (limited to 'libjack/shm.c')
-rw-r--r--libjack/shm.c237
1 files changed, 163 insertions, 74 deletions
diff --git a/libjack/shm.c b/libjack/shm.c
index ad9a419..aa1d98d 100644
--- a/libjack/shm.c
+++ b/libjack/shm.c
@@ -1,23 +1,35 @@
+/* This module provides a set of abstract shared memory interfaces
+ * with support using both System V and POSIX shared memory
+ * implementations. The code is divided into three sections:
+ *
+ * - common (interface-independent) code
+ * - POSIX implementation
+ * - System V implementation
+ *
+ * The implementation used is determined by whether USE_POSIX_SHM was
+ * set in the ./configure step.
+ */
+
/*
- Copyright (C) 2003 Paul Davis
- Copyright (C) 2004 Jack O'Quin
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Id$
-*/
+ * Copyright (C) 2003 Paul Davis
+ * Copyright (C) 2004 Jack O'Quin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
#include <config.h>
#include <unistd.h>
@@ -30,7 +42,10 @@
#include <dirent.h>
#include <sys/mman.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <sysdeps/ipc.h>
#include <sys/shm.h>
+#include <sys/sem.h>
#include <sysdeps/ipc.h>
#include <jack/shm.h>
@@ -49,14 +64,6 @@ static char *shmtype_name = "System V";
static int jack_access_registry (jack_shm_info_t *ri);
static void jack_remove_shm (jack_shm_id_t *id);
-/* This module supports both System V and POSIX shared memory
- * interfaces. The code is divided into three sections:
- *
- * - common (interface-independent) code
- * - POSIX implementation
- * - System V implementation
- */
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* common interface-independent section
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -72,18 +79,96 @@ jack_shm_info_t registry_info = { /* SHM info for the registry */
static jack_shm_header_t* jack_shm_header = NULL;
static jack_shm_registry_t* jack_shm_registry = NULL;
+/* jack_shm_lock_registry() serializes updates to the shared memory
+ * segment JACK uses to keep track of the SHM segements allocated to
+ * all its processes, including multiple servers.
+ *
+ * This is not a high-contention lock, but it does need to work across
+ * multiple processes. High transaction rates and realtime safety are
+ * not required. Any solution needs to at least be portable to POSIX
+ * and POSIX-like systems.
+ *
+ * We must be particularly careful to ensure that the lock be released
+ * if the owning process terminates abnormally. Otherwise, a segfault
+ * or kill -9 at the wrong moment could prevent JACK from ever running
+ * again on that machine until after a reboot.
+ */
+
+#define JACK_SEMAPHORE_KEY 0x282929
+#ifndef USE_POSIX_SHM
+#define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY
+#endif
+
+static int semid = -1;
+
+/* all semaphore errors are fatal -- issue message, but do not return */
+static void
+semaphore_error (char *msg)
+{
+ jack_error ("Fatal JACK semaphore error: %s (%s)",
+ msg, strerror (errno));
+ abort ();
+}
+
+static void
+semaphore_init ()
+{
+ key_t semkey = JACK_SEMAPHORE_KEY;
+ struct sembuf sbuf;
+ int create_flags = IPC_CREAT | IPC_EXCL
+ | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+ /* Get semaphore ID associated with this key. */
+ if ((semid = semget(semkey, 0, 0)) == -1) {
+
+ /* Semaphore does not exist - Create. */
+ if ((semid = semget(semkey, 1, create_flags)) != -1) {
+
+ /* Initialize the semaphore, allow one owner. */
+ sbuf.sem_num = 0;
+ sbuf.sem_op = 1;
+ sbuf.sem_flg = 0;
+ if (semop(semid, &sbuf, 1) == -1) {
+ semaphore_error ("semop");
+ }
+
+ } else if (errno == EEXIST) {
+ if ((semid = semget(semkey, 0, 0)) == -1) {
+ semaphore_error ("semget");
+ }
+
+ } else {
+ semaphore_error ("semget creation");
+ }
+ }
+}
+
+static inline void
+semaphore_add (int value)
+{
+ struct sembuf sbuf;
+
+ sbuf.sem_num = 0;
+ sbuf.sem_op = value;
+ sbuf.sem_flg = SEM_UNDO;
+ if (semop(semid, &sbuf, 1) == -1) {
+ semaphore_error ("semop");
+ }
+}
+
static void
jack_shm_lock_registry (void)
{
- /* XXX magic with semaphores here */
- //JOQ: this is really needed now with multiple servers
+ if (semid == -1)
+ semaphore_init ();
+
+ semaphore_add (-1);
}
static void
jack_shm_unlock_registry (void)
{
- /* XXX magic with semaphores here */
- //JOQ: this is really needed now with multiple servers
+ semaphore_add (1);
}
static void
@@ -124,7 +209,7 @@ jack_shm_validate_registry ()
jack_error ("incompatible shm registry (%s)", strerror (errno));
/* Apparently, this registry was created by an older JACK
- * version. Delete it and try again. */
+ * version. Delete it so we can try again. */
jack_release_shm (&registry_info);
jack_remove_shm (&registry_id);
@@ -194,11 +279,10 @@ jack_destroy_shm (jack_shm_info_t* si)
jack_shm_registry_t *
jack_get_free_shm_info ()
{
+ /* registry must be locked */
jack_shm_registry_t* si = NULL;
int i;
- jack_shm_lock_registry ();
-
for (i = 0; i < MAX_SHM_ID; ++i) {
if (jack_shm_registry[i].size == 0) {
break;
@@ -208,8 +292,6 @@ jack_get_free_shm_info ()
if (i < MAX_SHM_ID) {
si = &jack_shm_registry[i];
}
-
- jack_shm_unlock_registry ();
return si;
}
@@ -466,37 +548,39 @@ jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si)
{
jack_shm_registry_t* registry;
int shm_fd;
- int perm = O_RDWR|O_CREAT;
+ int rc = -1;
- if ((registry = jack_get_free_shm_info ()) == NULL) {
- return -1;
- }
+ jack_shm_lock_registry ();
- if ((shm_fd = shm_open (shm_name, O_RDWR|O_CREAT, 0666)) < 0) {
- jack_error ("cannot create shm segment %s (%s)", shm_name,
- strerror (errno));
- return -1;
- }
-
- if (perm & O_CREAT) {
- if (ftruncate (shm_fd, size) < 0) {
+ if ((registry = jack_get_free_shm_info ())) {
+
+ if ((shm_fd = shm_open (shm_name, O_RDWR|O_CREAT, 0666)) >= 0) {
+
+ if (ftruncate (shm_fd, size) >= 0) {
+
+ close (shm_fd);
+ registry->size = size;
+ snprintf (registry->id, sizeof (registry->id),
+ "%s", shm_name);
+ registry->allocator = getpid();
+ si->index = registry->index;
+ si->attached_at = MAP_FAILED; /* not attached */
+ rc = 0; /* success */
+
+ } else {
jack_error ("cannot set size of engine shm "
"registry 0 (%s)",
strerror (errno));
- return -1;
+ }
+
+ } else {
+ jack_error ("cannot create shm segment %s (%s)",
+ shm_name, strerror (errno));
}
}
- close (shm_fd);
-
- registry->size = size;
- snprintf (registry->id, sizeof (registry->id), "%s", shm_name);
- registry->allocator = getpid();
-
- si->index = registry->index;
- si->attached_at = MAP_FAILED; /* segment not attached */
-
- return 0;
+ jack_shm_unlock_registry ();
+ return rc;
}
int
@@ -652,28 +736,33 @@ jack_shmalloc (const char* name_not_used, jack_shmsize_t size,
{
int shmflags;
int shmid;
+ int rc = -1;
jack_shm_registry_t* registry;
- if ((registry = jack_get_free_shm_info ()) == NULL) {
- return -1;
- }
+ jack_shm_lock_registry ();
- shmflags = 0666 | IPC_CREAT | IPC_EXCL;
+ if ((registry = jack_get_free_shm_info ())) {
- if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) < 0) {
- jack_error ("cannot create shm segment %s (%s)",
- name_not_used, strerror (errno));
- return -1;
- }
+ shmflags = 0666 | IPC_CREAT | IPC_EXCL;
- registry->size = size;
- registry->id = shmid;
- registry->allocator = getpid();
+ if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) >= 0) {
- si->index = registry->index;
- si->attached_at = MAP_FAILED; /* segment not attached */
+ registry->size = size;
+ registry->id = shmid;
+ registry->allocator = getpid();
+ si->index = registry->index;
+ si->attached_at = MAP_FAILED; /* not attached */
+ rc = 0;
- return 0;
+ } else {
+ jack_error ("cannot create shm segment %s (%s)",
+ name_not_used, strerror (errno));
+ }
+ }
+
+ jack_shm_unlock_registry ();
+
+ return rc;
}
int