1717#include < thread>
1818#ifdef __linux__
1919#include < sys/ipc.h>
20+ #include < sys/sem.h>
2021#include < sys/shm.h>
2122#include < unistd.h>
23+ #include < cerrno>
24+ union semun { // not defined by glibc; needed for semctl SETVAL
25+ int val;
26+ struct semid_ds * buf;
27+ unsigned short * array;
28+ };
2229#endif
2330#include < cstring>
2431#include < cctype>
32+ #include < cstdint>
2533#include < regex>
2634
2735#include " concore_base.hpp"
@@ -41,6 +49,9 @@ class Concore{
4149 string outpath = " ./out" ;
4250
4351 static constexpr size_t SHM_SIZE = 4096 ;
52+ // SHM layout: [0..7]=uint64 seq#, [8..]=payload. See issue #195.
53+ static constexpr size_t SHM_HEADER_SIZE = 8 ;
54+ static constexpr size_t SHM_PAYLOAD_MAX = SHM_SIZE - SHM_HEADER_SIZE - 1 ;
4455
4556 int shmId_create = -1 ;
4657 int shmId_get = -1 ;
@@ -50,6 +61,11 @@ class Concore{
5061 // File sharing:- 0, Shared Memory:- 1
5162 int communication_iport = 0 ; // iport refers to input port
5263 int communication_oport = 0 ; // oport refers to input port
64+ #ifdef __linux__
65+ // POSIX semaphores keyed by (shm_key+1). Idempotent across processes.
66+ int semId_create = -1 ;
67+ int semId_get = -1 ;
68+ #endif
5369
5470#ifdef CONCORE_USE_ZMQ
5571 map<string, concore_base::ZeroMQPort*> zmq_ports;
@@ -145,6 +161,9 @@ class Concore{
145161 if (shmId_create != -1 ) {
146162 shmctl (shmId_create, IPC_RMID , nullptr );
147163 }
164+ if (semId_create != -1 ) {
165+ semctl (semId_create, 0 , IPC_RMID );
166+ }
148167#endif
149168 }
150169
@@ -166,13 +185,20 @@ class Concore{
166185 delay(other.delay), retrycount(other.retrycount), simtime(other.simtime),
167186 maxtime(other.maxtime), iport(std::move(other.iport)), oport(std::move(other.oport)),
168187 params(std::move(other.params))
188+ #ifdef __linux__
189+ , semId_create(other.semId_create), semId_get(other.semId_get)
190+ #endif
169191 {
170192 other.shmId_create = -1 ;
171193 other.shmId_get = -1 ;
172194 other.sharedData_create = nullptr ;
173195 other.sharedData_get = nullptr ;
174196 other.communication_iport = 0 ;
175197 other.communication_oport = 0 ;
198+ #ifdef __linux__
199+ other.semId_create = -1 ;
200+ other.semId_get = -1 ;
201+ #endif
176202 }
177203
178204 /* *
@@ -190,6 +216,8 @@ class Concore{
190216 shmdt (sharedData_get);
191217 if (shmId_create != -1 )
192218 shmctl (shmId_create, IPC_RMID , nullptr );
219+ if (semId_create != -1 )
220+ semctl (semId_create, 0 , IPC_RMID );
193221#endif
194222
195223 s = std::move (other.s );
@@ -209,13 +237,21 @@ class Concore{
209237 iport = std::move (other.iport );
210238 oport = std::move (other.oport );
211239 params = std::move (other.params );
240+ #ifdef __linux__
241+ semId_create = other.semId_create ;
242+ semId_get = other.semId_get ;
243+ #endif
212244
213245 other.shmId_create = -1 ;
214246 other.shmId_get = -1 ;
215247 other.sharedData_create = nullptr ;
216248 other.sharedData_get = nullptr ;
217249 other.communication_iport = 0 ;
218250 other.communication_oport = 0 ;
251+ #ifdef __linux__
252+ other.semId_create = -1 ;
253+ other.semId_get = -1 ;
254+ #endif
219255
220256 return *this ;
221257 }
@@ -254,6 +290,55 @@ class Concore{
254290 return std::stoi (numberString);
255291 }
256292
293+ #ifdef __linux__
294+ static inline uint64_t shm_load_seq (const char * base) {
295+ uint64_t v;
296+ __atomic_load (reinterpret_cast <const uint64_t *>(base), &v, __ATOMIC_ACQUIRE);
297+ return v;
298+ }
299+
300+ static inline void shm_store_seq (char * base, uint64_t v) {
301+ __atomic_store (reinterpret_cast <uint64_t *>(base), &v, __ATOMIC_RELEASE);
302+ }
303+
304+ static int shm_sem_create (key_t key) {
305+ int id = semget (key, 1 , IPC_CREAT | 0666 );
306+ if (id < 1 ) return -1 ;
307+ semun arg{};
308+ arg.val = 1 ;
309+ if (semctl (id, 0 , SETVAL , arg) < 0 ) return -1 ;
310+ return id;
311+ }
312+
313+ static void shm_sem_acquire (int id) {
314+ if (id < 0 ) return ;
315+ sembuf sb{};
316+ sb.sem_num = 0 ;
317+ sb.sem_op = -1 ;
318+ sb.sem_flg = 0 ;
319+ while (semop (id, &sb, 1 ) == -1 ) {
320+ if (errno != EINTR ) {
321+ std::cerr << " semop(acquire) failed errno=" << errno << std::endl;
322+ return ;
323+ }
324+ }
325+ }
326+
327+ static void shm_sem_release (int id) {
328+ if (id < 0 ) return ;
329+ sembuf sb{};
330+ sb.sem_num = 0 ;
331+ sb.sem_op = 1 ;
332+ sb.sem_flg = 0 ;
333+ while (semop (id, &sb, 1 ) == -1 ) {
334+ if (errno != EINTR ) {
335+ std::cerr << " semop(release) failed errno=" << errno << std::endl;
336+ return ;
337+ }
338+ }
339+ }
340+ #endif
341+
257342#ifdef __linux__
258343 /* *
259344 * @brief Creates a shared memory segment with the given key.
@@ -286,7 +371,17 @@ class Concore{
286371 if (sharedData_create == reinterpret_cast <char *>(-1 )) {
287372 std::cerr << " Failed to attach shared memory segment." << std::endl;
288373 sharedData_create = nullptr ;
374+ return ;
289375 }
376+
377+ semId_create = shm_sem_create (key + 1 );
378+ if (semId_create < 0 ) {
379+ std::cerr << " Failed to create shared memory semaphore." << std::endl;
380+ }
381+
382+ // initialise header
383+ shm_store_seq (sharedData_create, uint64_t {0 });
384+ sharedData_create[SHM_HEADER_SIZE ] = ' \0 ' ;
290385 }
291386
292387 /* *
@@ -321,6 +416,14 @@ class Concore{
321416 if (sharedData_get == reinterpret_cast <char *>(-1 )) {
322417 std::cerr << " Failed to attach shared memory segment." << std::endl;
323418 sharedData_get = nullptr ;
419+ return ;
420+ }
421+
422+ // attach reader-side semaphore (writer owns its lifetime)
423+ semId_get = semget (key + 1 , 1 , 0666 );
424+ if (semId_get < 0 ) {
425+ // no semaphore: reads fall back to seq# alone
426+ semId_get = -1 ;
324427 }
325428 }
326429#endif
@@ -504,34 +607,37 @@ class Concore{
504607 string ins = " " ;
505608 ReadStatus status = ReadStatus::SUCCESS ;
506609 try {
507- if (shmId_get != -1 ) {
508- if (sharedData_get && sharedData_get[0 ] != ' \0 ' ) {
509- std::string message (sharedData_get, strnlen (sharedData_get, SHM_SIZE ));
510- ins = message;
511- }
512- else
513- {
610+ if (shmId_get == -1 || sharedData_get == nullptr ) {
611+ status = ReadStatus::FILE_NOT_FOUND ;
514612 throw 505 ;
515613 }
516- }
517- else
518- {
519- status = ReadStatus::FILE_NOT_FOUND ;
520- throw 505 ;
521- }
614+ uint64_t seq = shm_load_seq (sharedData_get);
615+ if (seq == 0 ) {
616+ throw 505 ; // no write published yet
617+ }
618+ std::string message (
619+ sharedData_get + SHM_HEADER_SIZE ,
620+ strnlen (sharedData_get + SHM_HEADER_SIZE , SHM_PAYLOAD_MAX ));
621+ ins = message;
522622 } catch (...) {
523623 ins = initstr;
524624 if (status == ReadStatus::SUCCESS )
525625 status = ReadStatus::FILE_NOT_FOUND ;
526626 }
527-
627+
528628 int retry = 0 ;
529629 const int MAX_RETRY = 100 ;
530630 while ((int )ins.length ()==0 && retry < MAX_RETRY ){
531631 this_thread::sleep_for (timespan);
532632 try {
533- if (shmId_get != -1 ) {
534- std::string message (sharedData_get, strnlen (sharedData_get, SHM_SIZE ));
633+ if (shmId_get != -1 && sharedData_get != nullptr ) {
634+ uint64_t seq = shm_load_seq (sharedData_get);
635+ if (seq == 0 ) {
636+ throw 505 ;
637+ }
638+ std::string message (
639+ sharedData_get + SHM_HEADER_SIZE ,
640+ strnlen (sharedData_get + SHM_HEADER_SIZE , SHM_PAYLOAD_MAX ));
535641 ins = message;
536642 retrycount++;
537643 }
@@ -673,28 +779,38 @@ class Concore{
673779
674780 try {
675781 std::ostringstream outfile;
676- if (shmId_create != -1 ){
677- if (sharedData_create == nullptr )
678- throw 506 ;
679- val.insert (val.begin (),simtime+delta);
680- outfile<<' [' ;
681- for (int i=0 ;i<val.size ()-1 ;i++)
682- outfile<<val[i]<<' ,' ;
683- outfile<<val[val.size ()-1 ]<<' ]' ;
684- std::string result = outfile.str ();
685- if (result.size () >= SHM_SIZE ) {
686- std::cerr << " ERROR: write_SM payload (" << result.size ()
687- << " bytes) exceeds " << SHM_SIZE - 1
688- << " -byte shared memory limit. Data truncated!" << std::endl;
689- }
690- std::strncpy (sharedData_create, result.c_str (), SHM_SIZE - 1 );
691- sharedData_create[SHM_SIZE - 1 ] = ' \0 ' ;
692- // simtime must not be mutated here (issue #385).
693- }
694- else {
782+ if (shmId_create == -1 ){
695783 throw 505 ;
696- }
697784 }
785+ if (sharedData_create == nullptr )
786+ throw 506 ;
787+ val.insert (val.begin (),simtime+delta);
788+ outfile<<' [' ;
789+ for (int i=0 ;i<val.size ()-1 ;i++)
790+ outfile<<val[i]<<' ,' ;
791+ outfile<<val[val.size ()-1 ]<<' ]' ;
792+ std::string result = outfile.str ();
793+ const size_t max_payload = SHM_PAYLOAD_MAX ;
794+ if (result.size () > max_payload) {
795+ std::cerr << " ERROR: write_SM payload (" << result.size ()
796+ << " bytes) exceeds " << max_payload
797+ << " -byte shared memory limit. Data truncated!" << std::endl;
798+ result.resize (max_payload);
799+ }
800+ #ifdef __linux__
801+ shm_sem_acquire (semId_create);
802+ #endif
803+ std::memcpy (sharedData_create + SHM_HEADER_SIZE ,
804+ result.c_str (), result.size ());
805+ __atomic_thread_fence (__ATOMIC_RELEASE);
806+ (void )__atomic_fetch_add (
807+ reinterpret_cast <uint64_t *>(sharedData_create),
808+ uint64_t {1 }, __ATOMIC_ACQ_REL);
809+ #ifdef __linux__
810+ shm_sem_release (semId_create);
811+ #endif
812+ // simtime must not be mutated here (issue #385).
813+ }
698814
699815 catch (...){
700816 cout<<" skipping +" <<outpath<<port<<" /" <<name;
@@ -712,18 +828,31 @@ class Concore{
712828 chrono::milliseconds timespan ((int )(2000 *delay));
713829 this_thread::sleep_for (timespan);
714830 try {
715- if (shmId_create != -1 ){
716- if (sharedData_create == nullptr )
717- throw 506 ;
718- if (val.size () >= SHM_SIZE ) {
719- std::cerr << " ERROR: write_SM payload (" << val.size ()
720- << " bytes) exceeds " << SHM_SIZE - 1
721- << " -byte shared memory limit. Data truncated!" << std::endl;
722- }
723- std::strncpy (sharedData_create, val.c_str (), SHM_SIZE - 1 );
724- sharedData_create[SHM_SIZE - 1 ] = ' \0 ' ;
831+ if (shmId_create == -1 ){
832+ throw 505 ;
725833 }
726- else throw 505 ;
834+ if (sharedData_create == nullptr )
835+ throw 506 ;
836+ const size_t max_payload = SHM_PAYLOAD_MAX ;
837+ if (val.size () > max_payload) {
838+ std::cerr << " ERROR: write_SM payload (" << val.size ()
839+ << " bytes) exceeds " << max_payload
840+ << " -byte shared memory limit. Data truncated!" << std::endl;
841+ val.resize (max_payload);
842+ }
843+ #ifdef __linux__
844+ shm_sem_acquire (semId_create);
845+ #endif
846+ std::memcpy (sharedData_create + SHM_HEADER_SIZE ,
847+ val.c_str (), val.size ());
848+ sharedData_create[SHM_HEADER_SIZE + val.size ()] = ' \0 ' ;
849+ __atomic_thread_fence (__ATOMIC_RELEASE);
850+ (void )__atomic_fetch_add (
851+ reinterpret_cast <uint64_t *>(sharedData_create),
852+ uint64_t {1 }, __ATOMIC_ACQ_REL);
853+ #ifdef __linux__
854+ shm_sem_release (semId_create);
855+ #endif
727856 }
728857 catch (...){
729858 cout<<" skipping +" <<outpath<<port<<" /" <<name;
0 commit comments