GCC Code Coverage Report


Directory: src/
File: src/LB_comm/shmem.c
Date: 2026-09-15 07:37:49
Exec Total Coverage
Lines: 140 178 78.7%
Functions: 17 19 89.5%
Branches: 77 120 64.2%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2021 Barcelona Supercomputing Center */
3 /* */
4 /* This file is part of the DLB library. */
5 /* */
6 /* DLB is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU Lesser General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* DLB is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU Lesser General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU Lesser General Public License */
17 /* along with DLB. If not, see <https://www.gnu.org/licenses/>. */
18 /*********************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "LB_comm/shmem.h"
25
26 #include "support/debug.h"
27
28 #include <unistd.h>
29 #include <sys/mman.h>
30 #include <sys/wait.h>
31 #include <sys/types.h>
32 #include <sys/stat.h> /* For mode constants */
33 #include <fcntl.h> /* For O_* constants */
34 #include <signal.h>
35 #include <dirent.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <pthread.h>
41
42 #ifndef _POSIX_THREAD_PROCESS_SHARED
43 #error This system does not support process shared mutexes
44 #endif
45
46 #include "LB_comm/shmem.h"
47 #include "support/atomic.h"
48 #include "support/debug.h"
49 #include "support/options.h"
50 #include "support/mytime.h"
51 #include "support/mask_utils.h"
52
53 #define SHMEM_TIMEOUT_SECONDS 10
54
55 738 static bool shmem_consistency_check_pids(pid_t *pidlist, pid_t pid,
56 void (*cleanup_fn)(void*,int), void *shdata) {
57 738 bool registered = false;
58 int i;
59
2/2
✓ Branch 1 taken 6172 times.
✓ Branch 2 taken 738 times.
6910 for(i=0; i<mu_get_system_size(); ++i) {
60
2/2
✓ Branch 0 taken 5672 times.
✓ Branch 1 taken 500 times.
6172 if (pidlist[i] == 0) {
61
2/2
✓ Branch 0 taken 570 times.
✓ Branch 1 taken 5102 times.
5672 if (!registered) {
62 570 pidlist[i] = pid;
63 570 registered = true;
64 }
65 } else {
66
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 495 times.
500 if (kill(pidlist[i], 0) == -1) {
67 /* Process pidlist[i] is registered and does not exist */
68
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (cleanup_fn) {
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 verbose(VB_SHMEM,
70 "Process %d is registered in DLB but does not exist, probably"
71 " due to a bad termination of such process.\n"
72 "DLB is cleaning up the shared memory. If it fails,"
73 " please run 'dlb_shm --delete' and try again.", pidlist[i]);
74 5 cleanup_fn(shdata, pidlist[i]);
75 5 pidlist[i] = 0;
76 } else {
77 verbose(VB_SHMEM, "Process %d attached to shmem not found, "
78 "you may want to run \"dlb_shm -d\"", pidlist[i]);
79 }
80 }
81 }
82 }
83 738 return registered;
84 }
85
86 727 static bool shmem_consistency_remove_pid(pid_t *pidlist, pid_t pid) {
87 727 bool last_one = true;
88 int i;
89
2/2
✓ Branch 1 taken 6144 times.
✓ Branch 2 taken 727 times.
6871 for(i=0; i<mu_get_system_size(); ++i) {
90
2/2
✓ Branch 0 taken 561 times.
✓ Branch 1 taken 5583 times.
6144 if (pidlist[i] == pid) {
91 561 pidlist[i] = 0;
92
2/2
✓ Branch 0 taken 287 times.
✓ Branch 1 taken 5296 times.
5583 } else if (pidlist[i] != 0) {
93 287 last_one = false;
94 }
95 }
96 727 return last_one;
97 }
98
99 1476 static void shmem_consistency_check_version(unsigned int creator_version,
100 unsigned int process_version) {
101
2/2
✓ Branch 0 taken 1364 times.
✓ Branch 1 taken 112 times.
1476 if (creator_version != SHMEM_VERSION_IGNORE) {
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1364 times.
1364 fatal_cond(creator_version != process_version,
103 "The existing DLB shared memory version differs from the expected one.\n"
104 "This may have been caused by a DLB version upgrade in between runs.\n"
105 "Please, run 'dlb_shm --delete' and try again.\n"
106 "Contact us at " PACKAGE_BUGREPORT " if the issue persists.");
107 }
108 1476 }
109
110 738 static void get_shmem_filename(char *filename, const char *shmem_module,
111 const char *shmem_key, int shmem_color) {
112
3/4
✓ Branch 0 taken 738 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 734 times.
✓ Branch 3 taken 4 times.
738 if (shmem_key && shmem_key[0] != '\0') {
113
2/2
✓ Branch 0 taken 731 times.
✓ Branch 1 taken 3 times.
734 if (shmem_color <= 0) {
114 731 snprintf(filename, SHM_NAME_LENGTH, "/DLB_%s_%s",
115 shmem_module, shmem_key);
116 } else {
117 3 snprintf(filename, SHM_NAME_LENGTH, "/DLB_%s_%d_%s",
118 shmem_module, shmem_color, shmem_key);
119 }
120 } else {
121
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (shmem_color <= 0) {
122 4 snprintf(filename, SHM_NAME_LENGTH, "/DLB_%s_%d",
123 shmem_module, getuid());
124 } else {
125 snprintf(filename, SHM_NAME_LENGTH, "/DLB_%s_%d_%d",
126 shmem_module, shmem_color, getuid());
127 }
128 }
129 738 }
130
131
132 738 shmem_handler_t* shmem_init(void **shdata, const shmem_props_t *shmem_props) {
133 738 pid_t pid = getpid();
134 738 const char *shmem_module = shmem_props->name;
135
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 658 times.
738 verbose(VB_SHMEM, "Shared Memory Init: pid(%d), module(%s)", pid, shmem_module);
136
137 /* Allocate new Shared Memory handler */
138 738 shmem_handler_t *handler = malloc(sizeof(shmem_handler_t));
139
140 /* Calculate total shmem size:
141 * shmem = shsync + shdata
142 * shsync and shdata are both variable in size
143 */
144 738 size_t shsync_size = shmem_shsync__size();
145 738 size_t shdata_size = shmem_props->size;
146 738 handler->shm_size = shsync_size + shdata_size;
147
148 /* Get /dev/shm/ file names to create */
149 738 const char *shmem_key = shmem_props->key;
150 738 int shmem_color = shmem_props->color;
151 738 get_shmem_filename(handler->shm_filename, shmem_module, shmem_key, shmem_color);
152
153 /* Obtain a file descriptor for the shmem */
154 738 handler->fd = shm_open(handler->shm_filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 738 times.
738 if (handler->fd == -1) {
156 fatal("shm_open error: %s", strerror(errno));
157 }
158
159 /* Close fd on exec */
160
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 738 times.
738 if (fcntl(handler->fd, F_SETFD, FD_CLOEXEC) == -1) {
161 fatal("fcntl error: %s", strerror(errno));
162 }
163
164 /* Truncate the regular file to a precise size */
165
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 738 times.
738 if (ftruncate(handler->fd, handler->shm_size) == -1) {
166 fatal("ftruncate error: %s", strerror(errno));
167 }
168
169 /* Map shared memory object */
170 738 handler->shm_addr = mmap(NULL, handler->shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, handler->fd, 0);
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 738 times.
738 if (handler->shm_addr == MAP_FAILED) {
172 fatal("mmap error: %s", strerror(errno));
173 }
174
175 /* Set the address for both structs */
176 738 handler->shsync = (shmem_sync_t*) handler->shm_addr;
177 738 *shdata = handler->shm_addr + shsync_size;
178
179
2/2
✓ Branch 0 taken 409 times.
✓ Branch 1 taken 329 times.
738 if (__sync_bool_compare_and_swap(&handler->shsync->initializing, 0, 1)) {
180 /* Shared Memory creator */
181
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 364 times.
409 verbose(VB_SHMEM, "Initializing Shared Memory (%s)", shmem_module);
182
183 /* Init pthread mutex */
184 pthread_mutexattr_t attr;
185
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 409 times.
409 if (pthread_mutexattr_init(&attr) != 0) {
186 fatal("pthread_mutexattr_init error: %s", strerror(errno));
187 }
188
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 409 times.
409 if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) {
189 fatal("pthread_mutexattr_setpshared error: %s", strerror(errno));
190 }
191
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 409 times.
409 if (pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) != 0) {
192 fatal("pthread_mutexattr_setrobust error: %s", strerror(errno));
193 }
194
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 409 times.
409 if (pthread_mutex_init(&handler->shsync->shmem_mutex, &attr) != 0) {
195 fatal("pthread_mutex_init error: %s", strerror(errno));
196 }
197
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 409 times.
409 if (pthread_mutexattr_destroy(&attr) != 0) {
198 fatal("pthread_mutexattr_destroy error: %s", strerror(errno));
199 }
200
201 /* Set Shared Memory version */
202 409 handler->shsync->shmem_version = shmem_props->version;
203 409 handler->shsync->shsync_version = SHMEM_SYNC_VERSION;
204
205 409 handler->shsync->initialized = 1;
206 } else {
207 /* Shared Memory already created */
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 329 times.
329 while(!handler->shsync->initialized) __sync_synchronize();
209
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 309 times.
329 verbose(VB_SHMEM, "Attached to Shared Memory (%s)", shmem_module);
210 }
211
212 /* Check consistency */
213
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 673 times.
738 verbose(VB_SHMEM, "Checking shared memory consistency (%s)", shmem_module);
214 738 shmem_timedlock(handler);
215 738 shmem_consistency_check_version(handler->shsync->shsync_version, SHMEM_SYNC_VERSION);
216 738 shmem_consistency_check_version(handler->shsync->shmem_version, shmem_props->version);
217 738 shmem_consistency_check_pids(handler->shsync->pidlist, pid, shmem_props->cleanup_fn, *shdata);
218 738 shmem_unlock(handler);
219
220 738 return handler;
221 }
222
223 727 void shmem_finalize(shmem_handler_t* handler, bool (*is_empty_fn)(void)) {
224 #ifdef IS_BGQ_MACHINE
225 // BG/Q have some problems deallocating shmem
226 // It will be cleaned after the job completion anyway
227 return;
228 #endif
229
230 727 shmem_lock(handler);
231
4/4
✓ Branch 0 taken 442 times.
✓ Branch 1 taken 285 times.
✓ Branch 3 taken 301 times.
✓ Branch 4 taken 141 times.
727 bool is_empty = is_empty_fn ? is_empty_fn() : true;
232 727 bool is_last_one = shmem_consistency_remove_pid(handler->shsync->pidlist, getpid());
233
4/4
✓ Branch 0 taken 586 times.
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 451 times.
✓ Branch 3 taken 135 times.
727 bool delete_shmem = is_empty && is_last_one;
234 727 shmem_unlock(handler);
235
236 /* Here we should destroy the pthread mutex but another process may open
237 * the shared memory in this precise moment causing an invalid access to
238 * the mutex. */
239
240 /* All processes must unmap shmem */
241
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 727 times.
727 if (munmap(handler->shm_addr, handler->shm_size) != 0) {
242 fatal("munmap error: %s", strerror(errno));
243 }
244
245 /* Only the last process unlinks shmem */
246
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 276 times.
727 if (delete_shmem) {
247
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 406 times.
451 verbose(VB_SHMEM, "Removing shared memory %s", handler->shm_filename);
248
3/4
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 405 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 46 times.
451 if (shm_unlink(handler->shm_filename) != 0 && errno != ENOENT) {
249 fatal("shm_unlink error: %s", strerror(errno));
250 }
251 }
252
253 727 free(handler);
254 727 }
255
256 void shmem_detach_after_fork(shmem_handler_t *handler) {
257
258 if (handler->shm_addr) {
259 munmap(handler->shm_addr, handler->shm_size);
260 }
261
262 if (handler->fd >= 0) {
263 close(handler->fd);
264 }
265
266 free(handler);
267 }
268
269 738 void shmem_timedlock(shmem_handler_t *handler) {
270
271 struct timespec timeout;
272 738 get_time_real(&timeout);
273 738 timeout.tv_sec += SHMEM_TIMEOUT_SECONDS;
274
275 738 pthread_mutex_t *mutex = &handler->shsync->shmem_mutex;
276
277 738 int rc = pthread_mutex_timedlock(mutex, &timeout);
278
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 738 times.
738 if (rc == ETIMEDOUT) {
280 fatal("DLB cannot obtain the lock for the shared memory.\n"
281 "This may have been caused by a previous process crashing"
282 " while acquiring the DLB shared memory lock.\n"
283 "Please, run 'dlb_shm --delete' and try again.\n"
284 "Contact us at " PACKAGE_BUGREPORT " if the issue persists.");
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 738 times.
738 } else if (rc == EOWNERDEAD) {
286 if (pthread_mutex_consistent(mutex) != 0) {
287 perror("pthread_mutex_consistent failed");
288 pthread_mutex_unlock(mutex);
289 }
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 738 times.
738 } else if (rc != 0) {
291 fatal("pthread_mutex_timedlock error: %s", strerror(rc));
292 }
293 738 }
294
295 5275 void shmem_lock(shmem_handler_t *handler) {
296
297 5275 pthread_mutex_t *mutex = &handler->shsync->shmem_mutex;
298
299 5275 int rc = pthread_mutex_lock(mutex);
300
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5275 times.
5275 if (rc == EOWNERDEAD) {
302 // The previous owner died while holding the lock.
303 // Mark the mutex consistent again:
304 if (pthread_mutex_consistent(mutex) != 0) {
305 perror("pthread_mutex_consistent failed");
306 pthread_mutex_unlock(mutex);
307 }
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5275 times.
5275 } else if (rc != 0) {
309 fatal("pthread_mutex_lock error: %s", strerror(rc));
310 }
311
312 5275 }
313
314 6013 void shmem_unlock(shmem_handler_t *handler) {
315
316 6013 int rc = pthread_mutex_unlock(&handler->shsync->shmem_mutex);
317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6013 times.
6013 if (rc != 0) {
318 fatal("pthread_mutex_unlock error: %s", strerror(rc));
319 }
320 6013 }
321
322 /* Shared memory states (BUSY(0-n) <- READY(0-n) -> MAINTENANCE(1)):
323 * - READY: the shared memory can be locked or moved to another state.
324 * - BUSY: the shared memory is being used, each process still needs to
325 * use atomic operations or locks to access the data.
326 * This state prevents going to maintenance mode.
327 * - MAINTENANCE: only one process can set the state to maintenance,
328 * processes trying to set BUSY state will have to wait
329 *
330 * This system is useful if all processes want to begin a group operation
331 * (like a barrier) entering the BUSY state, and we want to prevent a new
332 * process to join the group until the shared memory goes back to READY.
333 */
334
335 enum { SHMEM_TRYAQUIRE_USECS = 100 };
336
337 /* Busy wait until the shmem can be locked with the state MAINTENANCE */
338 5 void shmem_lock_maintenance( shmem_handler_t* handler ) {
339 5 volatile shmem_state_t *state = &handler->shsync->state;
340 while(1) {
341 6 pthread_mutex_lock(&handler->shsync->shmem_mutex);
342
2/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 switch(*state) {
343 5 case SHMEM_READY:
344 /* Lock successfully acquired: READY -> MAINTENANCE */
345 5 *state = SHMEM_MAINTENANCE;
346 5 return;
347 1 case SHMEM_BUSY:
348 /* Shmem cannot be put in maintenance while BUSY */
349 1 pthread_mutex_unlock(&handler->shsync->shmem_mutex);
350 1 usleep(SHMEM_TRYAQUIRE_USECS);
351 1 break;
352 case SHMEM_MAINTENANCE:
353 /* This should not happen */
354 pthread_mutex_unlock(&handler->shsync->shmem_mutex);
355 fatal("Shared memory lock inconsistency. Please report to " PACKAGE_BUGREPORT);
356 break;
357 }
358 }
359 }
360
361 /* Unlock a previoulsy shmem in the MAINTENANCE state */
362 5 void shmem_unlock_maintenance( shmem_handler_t* handler ) {
363 /* Unlock MAINTENANCE -> READY */
364 5 int error = handler->shsync->state != SHMEM_MAINTENANCE;
365 5 handler->shsync->state = SHMEM_READY;
366 5 pthread_mutex_unlock(&handler->shsync->shmem_mutex);
367
368 /* This should not happen */
369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 fatal_cond(error, "Shared memory lock inconsistency. Please report to " PACKAGE_BUGREPORT);
370 5 }
371
372 /* Busy wait until the shmem can be set READY -> BUSY */
373 5 void shmem_acquire_busy( shmem_handler_t* handler ) {
374 5 volatile shmem_state_t *state = &handler->shsync->state;
375
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
5 while ( unlikely(
376 *state != SHMEM_BUSY
377 && !__sync_bool_compare_and_swap(state, SHMEM_READY, SHMEM_BUSY)
378 )) {
379 usleep(SHMEM_TRYAQUIRE_USECS);
380 }
381 5 }
382
383 /* Set shmm state BUSY -> READY */
384 3 void shmem_release_busy( shmem_handler_t* handler ) {
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 fatal_cond(
386 !__sync_bool_compare_and_swap(&handler->shsync->state, SHMEM_BUSY, SHMEM_READY),
387 "Shared memory lock inconsistency. Please report to " PACKAGE_BUGREPORT);
388 3 }
389
390 1 char *get_shm_filename( shmem_handler_t* handler ) {
391 1 return handler->shm_filename;
392 }
393
394 144 bool shmem_exists(const char *shmem_module, const char *shmem_key) {
395 char shm_filename[SHM_NAME_LENGTH*2];
396
3/4
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 141 times.
✓ Branch 3 taken 3 times.
144 if (shmem_key && shmem_key[0] != '\0') {
397 141 snprintf(shm_filename, SHM_NAME_LENGTH*2, "/dev/shm/DLB_%s_%s", shmem_module, shmem_key);
398 } else {
399 3 snprintf(shm_filename, SHM_NAME_LENGTH*2, "/dev/shm/DLB_%s_%d", shmem_module, getuid());
400 }
401 144 return access(shm_filename, F_OK) != -1;
402 }
403
404 void shmem_destroy(const char *shmem_module, const char *shmem_key) {
405 char shm_filename[SHM_NAME_LENGTH*2];
406 if (shmem_key && shmem_key[0] != '\0') {
407 snprintf(shm_filename, SHM_NAME_LENGTH*2, "/dev/shm/DLB_%s_%s", shmem_module, shmem_key);
408 } else {
409 snprintf(shm_filename, SHM_NAME_LENGTH*2, "/dev/shm/DLB_%s_%d", shmem_module, getuid());
410 }
411 shm_unlink(shm_filename);
412 }
413
414 1 int shmem_shsync__version(void) {
415 1 return SHMEM_SYNC_VERSION;
416 }
417
418 739 size_t shmem_shsync__size(void) {
419 739 size_t shsync_size = sizeof(shmem_sync_t) + sizeof(pid_t) * mu_get_system_size();
420 739 size_t alignment = DLB_CACHE_LINE; // in bytes
421 739 shsync_size = (shsync_size + (alignment - 1)) & ~(alignment - 1); // round up
422 739 return shsync_size;
423 }
424