Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save 27Cobalter/9d59b11171afd48de23ac410a4802d04 to your computer and use it in GitHub Desktop.

Select an option

Save 27Cobalter/9d59b11171afd48de23ac410a4802d04 to your computer and use it in GitHub Desktop.
#ifndef SHARED_MUTEX_H
#define SHARED_MUTEX_H
#include <pthread.h>
#include <stdatomic.h>
// 共有メモリに配置する構造体
typedef struct {
pthread_mutex_t mutex; // プロセス間で共有される堅牢なミューテックス
atomic_uint ref_count; // 参照カウンタ
} shared_data_t;
// 共有ミューテックスのハンドル
typedef struct {
int shm_fd; // 共有メモリのファイルディスクリプタ
const char *name; // 共有メモリ名
shared_data_t *data; // 共有データへのポインタ
} shared_mutex_t;
/**
* @brief 名前付き共有ミューテックスを作成またはオープンする。
* @param name ミューテックスの一意な名前 (例: "/my_app_mutex")
* @return 成功した場合はshared_mutex_tのポインタ、失敗した場合はNULL。
*/
shared_mutex_t* shared_mutex_create(const char *name);
/**
* @brief 共有ミューテックスをロックする。堅牢ミューテックスのため、戻り値の確認が必須。
* @param handle shared_mutex_createから返されたハンドル。
* @return 成功時は0、前の所有者がロックを保持したまま終了した場合はEOWNERDEAD、その他のエラーの場合は対応するエラーコード。
*/
int shared_mutex_lock(shared_mutex_t *handle);
/**
* @brief 共有ミューテックスのロックを解放する。
* @param handle shared_mutex_createから返されたハンドル。
*/
void shared_mutex_unlock(shared_mutex_t *handle);
/**
* @brief ミューテックスの状態を一貫性のあるものとしてマークする。EOWNERDEADを受け取った後に呼び出す。
* @param handle shared_mutex_createから返されたハンドル。
* @return 成功時は0、エラーの場合は対応するエラーコード。
*/
int shared_mutex_make_consistent(shared_mutex_t *handle);
/**
* @brief 共有ミューテックスのハンドルを解放し、必要であれば共有リソースの後片付けを行う。
* @param handle shared_mutex_createから返されたハンドル。
*/
void shared_mutex_destroy(shared_mutex_t *handle);
#endif // SHARED_MUTEX_H
#include "shared_mutex.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/file.h>
#define LOCK_FILE_SUFFIX ".lock"
// 補助ロックファイルを取得し、排他ロックをかけるヘルパー関数
static int acquire_init_lock(const char* base_name, char* lock_path_buf, size_t buf_size) {
snprintf(lock_path_buf, buf_size, "/tmp%s%s", base_name, LOCK_FILE_SUFFIX);
int lock_fd = open(lock_path_buf, O_CREAT | O_RDWR, 0666);
if (lock_fd == -1) {
perror("open lock file");
return -1;
}
if (flock(lock_fd, LOCK_EX) == -1) {
perror("flock");
close(lock_fd);
return -1;
}
return lock_fd;
}
// 補助ロックを解放するヘルパー関数
static void release_init_lock(int lock_fd) {
flock(lock_fd, LOCK_UN);
close(lock_fd);
}
shared_mutex_t* shared_mutex_create(const char *name) {
char lock_path[256];
int lock_fd = -1;
int shm_fd = -1;
shared_data_t *data = MAP_FAILED;
shared_mutex_t *handle = NULL;
int is_new = 0;
lock_fd = acquire_init_lock(name, lock_path, sizeof(lock_path));
if (lock_fd == -1) return NULL;
shm_fd = shm_open(name, O_RDWR | O_CREAT, 0666);
if (shm_fd == -1) {
perror("shm_open");
goto error;
}
struct stat shm_stat;
if (fstat(shm_fd, &shm_stat) == -1) {
perror("fstat");
goto error;
}
if (shm_stat.st_size == 0) {
is_new = 1;
if (ftruncate(shm_fd, sizeof(shared_data_t)) == -1) {
perror("ftruncate");
goto error;
}
}
data = (shared_data_t*)mmap(NULL, sizeof(shared_data_t),
PROT_READ | PROT_WRITE,
MAP_SHARED, shm_fd, 0);
if (data == MAP_FAILED) {
perror("mmap");
goto error;
}
if (is_new) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&data->mutex, &attr);
pthread_mutexattr_destroy(&attr);
atomic_init(&data->ref_count, 0);
}
release_init_lock(lock_fd);
lock_fd = -1;
atomic_fetch_add(&data->ref_count, 1);
handle = malloc(sizeof(shared_mutex_t));
handle->shm_fd = shm_fd;
handle->name = name;
handle->data = data;
return handle;
error:
if (data != MAP_FAILED) munmap(data, sizeof(shared_data_t));
if (shm_fd != -1) close(shm_fd);
if (lock_fd != -1) release_init_lock(lock_fd);
if (is_new) shm_unlink(name);
return NULL;
}
int shared_mutex_lock(shared_mutex_t *handle) {
if (handle && handle->data) {
return pthread_mutex_lock(&handle->data->mutex);
}
return EINVAL;
}
void shared_mutex_unlock(shared_mutex_t *handle) {
if (handle && handle->data) {
pthread_mutex_unlock(&handle->data->mutex);
}
}
int shared_mutex_make_consistent(shared_mutex_t *handle) {
if (handle && handle->data) {
return pthread_mutex_consistent(&handle->data->mutex);
}
return EINVAL;
}
void shared_mutex_destroy(shared_mutex_t *handle) {
if (!handle) return;
if (atomic_fetch_sub(&handle->data->ref_count, 1) == 1) {
char lock_path[256];
int lock_fd = acquire_init_lock(handle->name, lock_path, sizeof(lock_path));
if (lock_fd != -1) {
if (atomic_load(&handle->data->ref_count) == 0) {
printf("Process %d: This is the last process. Cleaning up all shared resources.\n", getpid());
pthread_mutex_destroy(&handle->data->mutex);
shm_unlink(handle->name);
unlink(lock_path);
}
release_init_lock(lock_fd);
} else {
fprintf(stderr, "Warning: Failed to acquire lock for cleanup. Resources may be leaked.\n");
}
}
munmap(handle->data, sizeof(shared_data_t));
close(handle->shm_fd);
free(handle);
}
#include "shared_mutex.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define MUTEX_NAME "/my_robust_mutex_1234"
// ロックを取得し、作業を行う関数
void perform_work(const char* process_name) {
printf("[%s:%d] Trying to create/open shared mutex...\n", process_name, getpid());
shared_mutex_t *mutex = shared_mutex_create(MUTEX_NAME);
if (!mutex) {
fprintf(stderr, "[%s:%d] FATAL: Failed to create mutex.\n", process_name, getpid());
return;
}
printf("[%s:%d] Trying to lock...\n", process_name, getpid());
int ret = shared_mutex_lock(mutex);
if (ret == EOWNERDEAD) {
fprintf(stderr, "[%s:%d] WARNING: Previous owner died holding the lock! State may be inconsistent.\n", process_name, getpid());
printf("[%s:%d] Attempting to recover the mutex state...\n", process_name, getpid());
// ★★★ 本来はここで共有データの一貫性を修復する処理を行う ★★★
shared_mutex_make_consistent(mutex);
printf("[%s:%d] Mutex state recovered. Lock is now held.\n", process_name, getpid());
} else if (ret != 0) {
fprintf(stderr, "[%s:%d] FATAL: Failed to lock mutex: %s\n", process_name, getpid(), strerror(ret));
shared_mutex_destroy(mutex);
return;
}
printf("[%s:%d] Lock acquired successfully. Working for 3 seconds...\n", process_name, getpid());
sleep(3);
printf("[%s:%d] Work done. Unlocking.\n", process_name, getpid());
shared_mutex_unlock(mutex);
printf("[%s:%d] Destroying handle.\n", process_name, getpid());
shared_mutex_destroy(mutex);
}
// ロックを取得した直後にクラッシュする関数
void perform_work_and_crash(const char* process_name) {
printf("[%s:%d] Trying to create/open shared mutex...\n", process_name, getpid());
shared_mutex_t *mutex = shared_mutex_create(MUTEX_NAME);
if (!mutex) {
fprintf(stderr, "[%s:%d] FATAL: Failed to create mutex.\n", process_name, getpid());
return;
}
printf("[%s:%d] Trying to lock...\n", process_name, getpid());
int ret = shared_mutex_lock(mutex);
if (ret != 0) {
fprintf(stderr, "[%s:%d] FATAL: Could not acquire lock before crashing: %s\n", process_name, getpid(), strerror(ret));
shared_mutex_destroy(mutex);
return;
}
printf("[%s:%d] Lock acquired. Crashing deliberately in 1 second...\n", process_name, getpid());
sleep(1);
// SIGKILLで強制終了(クラッシュをシミュレート)
raise(SIGKILL);
}
int main() {
// 念のため、起動時に古いリソースをクリーンアップ
shm_unlink(MUTEX_NAME);
char lock_path[256];
snprintf(lock_path, sizeof(lock_path), "/tmp%s.lock", MUTEX_NAME);
unlink(lock_path);
printf("--- TEST 1: Normal concurrent access ---\n");
pid_t pid1 = fork();
if (pid1 == 0) {
perform_work("Process A");
exit(0);
}
sleep(1); // 少しタイミングをずらす
pid_t pid2 = fork();
if (pid2 == 0) {
perform_work("Process B");
exit(0);
}
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
printf("\n--- TEST 1: Finished ---\n\n");
sleep(2);
printf("--- TEST 2: Crash and Recovery Scenario ---\n");
pid_t pid_crasher = fork();
if (pid_crasher == 0) {
// このプロセスはロックを取得後クラッシュする
perform_work_and_crash("Crasher");
exit(0); // ここには到達しない
}
// クラッシュするプロセスがロックを取得するのを待つ
sleep(3);
pid_t pid_recoverer = fork();
if (pid_recoverer == 0) {
// このプロセスがEOWNERDEADを検知し、回復を試みる
perform_work("Recoverer");
exit(0);
}
waitpid(pid_crasher, NULL, 0);
waitpid(pid_recoverer, NULL, 0);
printf("\n--- TEST 2: Finished ---\n\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment