diff options
author | Tim O'Shea <tim.oshea753@gmail.com> | 2013-07-22 15:42:07 -0400 |
---|---|---|
committer | Johnathan Corgan <johnathan@corganlabs.com> | 2013-07-22 13:55:45 -0700 |
commit | 4c860926e164310bbb7d4c83bba3308419bd0677 (patch) | |
tree | f91d97bed3e90a6ff49f34a1b2599f52819b6e2f /gnuradio-runtime | |
parent | 28e4e6bce03aa4e5f69b9f37dbac7e4d2230961d (diff) |
runtime: vmcircbuf_sysv_shm workaround for shmem race condition
Diffstat (limited to 'gnuradio-runtime')
-rw-r--r-- | gnuradio-runtime/lib/vmcircbuf_sysv_shm.cc | 180 |
1 files changed, 95 insertions, 85 deletions
diff --git a/gnuradio-runtime/lib/vmcircbuf_sysv_shm.cc b/gnuradio-runtime/lib/vmcircbuf_sysv_shm.cc index f26edf44fc..4d8741807b 100644 --- a/gnuradio-runtime/lib/vmcircbuf_sysv_shm.cc +++ b/gnuradio-runtime/lib/vmcircbuf_sysv_shm.cc @@ -40,6 +40,7 @@ #include <stdio.h> #include "pagesize.h" +#define MAX_SYSV_SHM_ATTEMPTS 3 namespace gr { @@ -60,93 +61,102 @@ namespace gr { throw std::runtime_error("gr::vmcircbuf_sysv_shm"); } - int shmid_guard = -1; - int shmid1 = -1; - int shmid2 = -1; - - // We use this as a guard page. We'll map it read-only on both ends of the buffer. - // Ideally we'd map it no access, but I don't think that's possible with SysV - if((shmid_guard = shmget(IPC_PRIVATE, pagesize, IPC_CREAT | 0400)) == -1) { - perror("gr::vmcircbuf_sysv_shm: shmget (0)"); - throw std::runtime_error("gr::vmcircbuf_sysv_shm"); - } - - if((shmid2 = shmget(IPC_PRIVATE, 2 * size + 2 * pagesize, IPC_CREAT | 0700)) == -1) { - perror("gr::vmcircbuf_sysv_shm: shmget(1)"); - shmctl(shmid_guard, IPC_RMID, 0); - throw std::runtime_error ("gr::vmcircbuf_sysv_shm"); - } - - if((shmid1 = shmget(IPC_PRIVATE, size, IPC_CREAT | 0700)) == -1) { - perror("gr::vmcircbuf_sysv_shm: shmget (2)"); - shmctl(shmid_guard, IPC_RMID, 0); - shmctl(shmid2, IPC_RMID, 0); - throw std::runtime_error("gr::vmcircbuf_sysv_shm"); - } - - void *first_copy = shmat (shmid2, 0, 0); - if(first_copy == (void *) -1) { - perror("gr::vmcircbuf_sysv_shm: shmat(1)"); - shmctl(shmid_guard, IPC_RMID, 0); - shmctl(shmid2, IPC_RMID, 0); - shmctl(shmid1, IPC_RMID, 0); - throw std::runtime_error("gr::vmcircbuf_sysv_shm"); - } - - shmctl(shmid2, IPC_RMID, 0); - - // There may be a race between our detach and attach. - // - // If the system allocates all shared memory segments at the same - // virtual addresses in all processes and if the system allocates - // some other segment to first_copy or first_copoy + size between - // our detach and attach, the attaches below could fail [I've never - // seen it fail for this reason]. - shmdt(first_copy); - - // first read-only guard page - if(shmat(shmid_guard, first_copy, SHM_RDONLY) == (void *) -1) { - perror("gr::vmcircbuf_sysv_shm: shmat(2)"); - shmctl(shmid_guard, IPC_RMID, 0); - shmctl(shmid1, IPC_RMID, 0); - throw std::runtime_error("gr::vmcircbuf_sysv_shm"); + // Attempt to allocate buffers (handle bad_alloc errors) + int attempts_remain(MAX_SYSV_SHM_ATTEMPTS); + while(attempts_remain-- > 0){ + + int shmid_guard = -1; + int shmid1 = -1; + int shmid2 = -1; + + // We use this as a guard page. We'll map it read-only on both ends of the buffer. + // Ideally we'd map it no access, but I don't think that's possible with SysV + if((shmid_guard = shmget(IPC_PRIVATE, pagesize, IPC_CREAT | 0400)) == -1) { + perror("gr::vmcircbuf_sysv_shm: shmget (0)"); + continue; + } + + if((shmid2 = shmget(IPC_PRIVATE, 2 * size + 2 * pagesize, IPC_CREAT | 0700)) == -1) { + perror("gr::vmcircbuf_sysv_shm: shmget(1)"); + shmctl(shmid_guard, IPC_RMID, 0); + continue; + } + + if((shmid1 = shmget(IPC_PRIVATE, size, IPC_CREAT | 0700)) == -1) { + perror("gr::vmcircbuf_sysv_shm: shmget (2)"); + shmctl(shmid_guard, IPC_RMID, 0); + shmctl(shmid2, IPC_RMID, 0); + continue; + } + + void *first_copy = shmat (shmid2, 0, 0); + if(first_copy == (void *) -1) { + perror("gr::vmcircbuf_sysv_shm: shmat(1)"); + shmctl(shmid_guard, IPC_RMID, 0); + shmctl(shmid2, IPC_RMID, 0); + shmctl(shmid1, IPC_RMID, 0); + continue; + } + + shmctl(shmid2, IPC_RMID, 0); + + // There may be a race between our detach and attach. + // + // If the system allocates all shared memory segments at the same + // virtual addresses in all processes and if the system allocates + // some other segment to first_copy or first_copoy + size between + // our detach and attach, the attaches below could fail [I've never + // seen it fail for this reason]. + shmdt(first_copy); + + // first read-only guard page + if(shmat(shmid_guard, first_copy, SHM_RDONLY) == (void *) -1) { + perror("gr::vmcircbuf_sysv_shm: shmat(2)"); + shmctl(shmid_guard, IPC_RMID, 0); + shmctl(shmid1, IPC_RMID, 0); + continue; + } + + // first copy + if(shmat (shmid1, (char*)first_copy + pagesize, 0) == (void *) -1) { + perror("gr::vmcircbuf_sysv_shm: shmat (3)"); + shmctl(shmid_guard, IPC_RMID, 0); + shmctl(shmid1, IPC_RMID, 0); + shmdt(first_copy); + continue; + } + + // second copy + if(shmat (shmid1, (char*)first_copy + pagesize + size, 0) == (void *) -1) { + perror("gr::vmcircbuf_sysv_shm: shmat (4)"); + shmctl(shmid_guard, IPC_RMID, 0); + shmctl(shmid1, IPC_RMID, 0); + shmdt((char *)first_copy + pagesize); + continue; + } + + // second read-only guard page + if(shmat(shmid_guard, (char*)first_copy + pagesize + 2 * size, SHM_RDONLY) == (void *) -1) { + perror("gr::vmcircbuf_sysv_shm: shmat(5)"); + shmctl(shmid_guard, IPC_RMID, 0); + shmctl(shmid1, IPC_RMID, 0); + shmdt(first_copy); + shmdt((char *)first_copy + pagesize); + shmdt((char *)first_copy + pagesize + size); + continue; + } + + shmctl(shmid1, IPC_RMID, 0); + shmctl(shmid_guard, IPC_RMID, 0); + + // Now remember the important stuff + d_base = (char*)first_copy + pagesize; + d_size = size; + break; } - - // first copy - if(shmat (shmid1, (char*)first_copy + pagesize, 0) == (void *) -1) { - perror("gr::vmcircbuf_sysv_shm: shmat (3)"); - shmctl(shmid_guard, IPC_RMID, 0); - shmctl(shmid1, IPC_RMID, 0); - shmdt(first_copy); - throw std::runtime_error("gr::vmcircbuf_sysv_shm"); + if(attempts_remain<0){ + throw std::runtime_error("gr_vmcircbuf_sysv_shm"); } - - // second copy - if(shmat (shmid1, (char*)first_copy + pagesize + size, 0) == (void *) -1) { - perror("gr::vmcircbuf_sysv_shm: shmat (4)"); - shmctl(shmid_guard, IPC_RMID, 0); - shmctl(shmid1, IPC_RMID, 0); - shmdt((char *)first_copy + pagesize); - throw std::runtime_error("gr::vmcircbuf_sysv_shm"); - } - - // second read-only guard page - if(shmat(shmid_guard, (char*)first_copy + pagesize + 2 * size, SHM_RDONLY) == (void *) -1) { - perror("gr::vmcircbuf_sysv_shm: shmat(5)"); - shmctl(shmid_guard, IPC_RMID, 0); - shmctl(shmid1, IPC_RMID, 0); - shmdt(first_copy); - shmdt((char *)first_copy + pagesize); - shmdt((char *)first_copy + pagesize + size); - throw std::runtime_error ("gr::vmcircbuf_sysv_shm"); - } - - shmctl(shmid1, IPC_RMID, 0); - shmctl(shmid_guard, IPC_RMID, 0); - - // Now remember the important stuff - d_base = (char*)first_copy + pagesize; - d_size = size; #endif } |