読者です 読者をやめる 読者になる 読者になる

Apacheモジュールで排他制御を行う

前回,Apacheモジュールで共有メモリを使うには - 房総爆釣日記で作ったモジュールに排他制御の機能を追加します.
まず,次の二つのヘッダファイルを追加します.

#include "apr_global_mutex.h"
#include "unixd.h"

ServerConfig構造体に,mutexが使うファイルの名前と,mutexのアドレスを格納するメンバを追加します.

typedef struct ServerConfig{
        char *mutex_path;
        apr_global_mutex_t *mutex;

        char *shm_path;
        apr_shm_t *shm_data;

        int *access_count;
}sconfig;

post_configステージでcreate mutexします.

                //Create global mutex
                status = apr_global_mutex_create(&(config->mutex), config->mutex_path, APR_LOCK_DEFAULT, p);
                if(status != APR_SUCCESS){
                        DEBUGLOG("create gloval mutex faild");
                        return HTTP_INTERNAL_SERVER_ERROR;
                }
#ifdef AP_NEED_SET_MUTEX_PERMS
                status = unixd_set_global_mutex_perms(config->mutex);
                if(status != APR_SUCCESS){
                        DEBUGLOG("Parent could not set permissions on globalmutex");
                        return HTTP_INTERNAL_SERVER_ERROR;
                }
#endif

ここのifdefでは,mutexのパーミッションの設定が必要かどうかを,AP_NEED_SET_MUTEX_PERMSで判定している模様.
unix系のOSではこの記述が必要らしい.
私の環境(Linux)ではこの記述がないとmutexの作成に失敗しました.

child_initステージでmutexにアタッチ

                       if(apr_global_mutex_child_init(&config->mutex, config->mutex_path, p))
                                DEBUGLOG("global mutex attached!");

ここまででmutexを使う準備が完了.実際に排他制御するときはクリティカルセクションの初めで

       status = apr_global_mutex_lock(config->mutex);

とします.戻り値をみてロックが成功したかどうか判定してやる必要があります.
ロックの解除はこれ.

       status = apr_global_mutex_lock(config->mutex);

全体のソースはこちら

#include <sys/types.h>
#include <unistd.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

#include "http_log.h"
#include "apr_time.h"
#include "apr_shm.h"
#include "apr_global_mutex.h"
#include "unixd.h"

#define USER_DATA_KEY "shm_counter"
#define DEBUGLOG(...) ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL, __VA_ARGS__)
//#define AP_NEED_SET_MUTEX_PERMS

module AP_MODULE_DECLARE_DATA shm_counter_module;

typedef struct ServerConfig{
        char *mutex_path;
        apr_global_mutex_t *mutex;

        char *shm_path;
        apr_shm_t *shm_data;

        int *access_count;
}sconfig;

static void *create_server_config(apr_pool_t *p, server_rec *s){
        sconfig *config;
        config = (sconfig *)(apr_palloc(p, sizeof(sconfig)));

        config->mutex_path = "/tmp/shm_counter_mutex";
        config->mutex = NULL;

        config->shm_path = "/tmp/shm_counter";
        config->shm_data = NULL;

        return config;
}

static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s){
        sconfig *config;
        void *user_data;
        apr_status_t status;

        apr_pool_userdata_get(&user_data, USER_DATA_KEY, s->process->pool);

        if(user_data == NULL){
                apr_pool_userdata_set((const void *)1, USER_DATA_KEY, apr_pool_cleanup_null, s->process->pool);
                return OK;
        }

        do{
                config = (sconfig *)(ap_get_module_config(s->module_config, &shm_counter_module));
                
                //Create global mutex
                status = apr_global_mutex_create(&(config->mutex), config->mutex_path, APR_LOCK_DEFAULT, p);
                if(status != APR_SUCCESS){
                        DEBUGLOG("create gloval mutex faild");
                        return HTTP_INTERNAL_SERVER_ERROR;
                }
#ifdef AP_NEED_SET_MUTEX_PERMS
                status = unixd_set_global_mutex_perms(config->mutex);
                if(status != APR_SUCCESS){
                        DEBUGLOG("Parent could not set permissions on globalmutex");
                        return HTTP_INTERNAL_SERVER_ERROR;
                }
#endif


                //Remove existing shared memory
                status = apr_shm_remove(config->shm_path, p);
                if (status == APR_SUCCESS) {
                        DEBUGLOG("removed existing shared memory file");
                }

                //Create shared memory
                status = apr_shm_create(&(config->shm_data), sizeof(int), config->shm_path, p);
                if(status != APR_SUCCESS){
                        return HTTP_INTERNAL_SERVER_ERROR;
                }

                config->access_count = (int *)(apr_shm_baseaddr_get(config->shm_data));
                *(config->access_count) = 0;

        }while((s=s->next) != NULL);

        return OK;
}

static void init_child(apr_pool_t *p, server_rec *s)
{
        sconfig *config;
        do{
                config = (sconfig *)(ap_get_module_config(s->module_config, &shm_counter_module));

                if(!config->shm_data){

                        if(apr_global_mutex_child_init(&config->mutex, config->mutex_path, p))
                                DEBUGLOG("global mutex attached!");


                        if(apr_shm_attach(&(config->shm_data), config->shm_path, p) != APR_SUCCESS)
                                DEBUGLOG("shm attached!");

                        return;
                }
        }while((s = s->next) != NULL);

}
static int shm_counter_handler(request_rec *r)
{
        sconfig *config;
        apr_status_t status = APR_SUCCESS;
        pid_t pid = getpid();

        config = (sconfig *)(ap_get_module_config(r->server->module_config, &shm_counter_module));
        //raise(SIGTRAP);
        status = apr_global_mutex_lock(config->mutex);
        if(status == APR_SUCCESS){
                DEBUGLOG("global mutex locked");
                (*config->access_count)++;
                DEBUGLOG("PID:%d ACCESS_COUNT=%d", pid, *config->access_count);
        }else{
                DEBUGLOG("global mutex lock faild");
                return HTTP_INTERNAL_SERVER_ERROR;
        }
        status = apr_global_mutex_unlock(config->mutex);
        DEBUGLOG("global mutex unlocked");

        return OK;
}

static void shm_counter_register_hooks(apr_pool_t *p)
{
        ap_hook_access_checker(shm_counter_handler, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_child_init(init_child, NULL, NULL, APR_HOOK_MIDDLE);
}

/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA shm_counter_module = {
        STANDARD20_MODULE_STUFF,
        NULL,                  /* create per-dir    config structures */
        NULL,                  /* merge  per-dir    config structures */
        create_server_config,  /* create per-server config structures */
        NULL,                  /* merge  per-server config structures */
        NULL,                  /* table of config file commands       */
        shm_counter_register_hooks  /* register hooks                      */
};