Newer
Older
c-interpreter / modules / libkc / src / kc_memory.c
Nomura Kei on 8 Jun 2023 27 KB UPDATE
////////////////////////////////////////////////////////////////////////////////
//
//  メモリ管理モジュール
//  @copyright  2003 - 2023  Nomura Kei
//

#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <threads.h>


// 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。
#ifdef KC_MEMORY_ENABLED
#undef KC_MEMORY_ENABLED
#endif

// KC_MEMORY_DUMP_LEAK が未定義の場合 0:無効 を定義する。
#ifndef KC_MEMORY_DUMP_LEAK
#define KC_MEMORY_DUMP_LEAK (0)
#endif

#include <kc_memory.h>



////////////////////////////////////////////////////////////////////////////////
//
//  定数定義
//
/** パディング	*/
#define KC_MEMORY_PADDING (sizeof(void*) * 2)


////////////////////////////////////////////////////////////////////////////////
//
//  内部変数
//
static KcMemoryHandler kc_memory_ahandler = NULL;		//<! メモリ確保時に呼び出されるハンドラ
static KcMemoryHandler kc_memory_fhandler = NULL;		//<! メモリ解放時に呼び出されるハンドラ
static KcMemoryHandler kc_memory_ehandler = NULL;		//<! メモリ確保/解放エラー発生時に呼び出されるハンドラ
static KcMemoryEntry   kc_memory_head;					//<! 管理メモリの先頭
static KcMemoryEntry   kc_memory_tail;					//<! 管理メモリの末尾
static KcMemoryEntry   kc_memory_error;					//<! 一時エラー出力用

static mtx_t kc_memory_mutex;							//<! 同期実行のための mutex


////////////////////////////////////////////////////////////////////////////////
//
//  内部関数 プロトタイプ宣言
//

// =============================================================================
//  ユーティリティで利用されるハンドラ関数
static bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg);
static bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg);
static bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg);

// =============================================================================
//  メモリ確保/解放
static void* kc_memory_allocate(size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);

static void* kc_memory_reallocate(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);

static void* kc_memory_reallocate_managed_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);

static void* kc_memory_reallocate_unmanaged_ptr(void* ptr, size_t size,
	   	KcMemoryMark mark, const char* file, const char* func, int line);

static void* kc_memory_reallocate_invalid_ptr(void* ptr, size_t size,
	   	KcMemoryMark mark, const char* file, const char* func, int line);

static void  kc_memory_deallocate(void* ptr);

static void kc_memory_deallocate_entry(KcMemoryEntry* entry);


// =============================================================================
//  メモリエントリ管理
static void kc_memory_init_entry(void);
static void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark mark, const char* file, const char* func, int line);
static void kc_memory_add_entry(KcMemoryEntry* entry);
static bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg);
static void kc_memory_remove_entry(KcMemoryEntry* entry);
static bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg);

// =============================================================================
//  ハンドラ実行
static void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg);
static void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg);
static void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg);

// ============================================================================
//  同期実行
static bool kc_memory_mutex_init(void);
static bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg);

// ============================================================================
//  データダンプ
static const char* kc_memory_strmark(int mark);
static void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size);
static void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size);



////////////////////////////////////////////////////////////////////////////////
//
//  公開関数群
//


/**
 * メモリ確保/解放/エラー発生時に呼び出されるハンドラ関数a登録します。
 *
 * @param ahandler メモリ確保時に呼び出されるハンドラ
 * @param fhandler メモリ解放時に呼び出されるハンドラ
 * @param ehandler メモリ確保/解放エラー発生時に呼び出されるハンドラ
 */
void kc_memory_set_handlers(KcMemoryHandler ahandler, KcMemoryHandler fhandler, KcMemoryHandler ehandler)
{
	kc_memory_ahandler = ahandler;
	kc_memory_fhandler = fhandler;
	kc_memory_ehandler = ehandler;
}



/**
 * 指定されたサイズのメモリを確保します。
 *
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
void* kc_memory_malloc(size_t size, const char* file, const char* func, int line)
{
	void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line);
	return ptr;
}


/**
 * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。
 *
 * @param nmemb 確保する要素数
 * @param size  1要素のメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line)
{
	size_t n = nmemb * size;
	void* ptr = kc_memory_allocate(n, KC_MEMORY_ALLOCATED, file, func, line);
	if (ptr != NULL)
	{
		memset(ptr, 0x00, n);
	}
	return ptr;
}


/**
 * ポインタが示すメモリブロックのサイズを size バイトに変更します。
 *
 * @param ptr   ポインタ
 * @param size  変更後のメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
void* kc_memory_realloc(void* ptr, size_t size, const char* file, const char* func, int line)
{
	void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line);
	return nptr;
}


/**
 * 指定されたメモリを解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
void kc_memory_free(void* ptr)
{
	kc_memory_deallocate(ptr);
}


// entries
/**
 * 指定された handler に現在管理しているメモリエントリが順次渡されます。
 * handler の中では、メモリエントリの情報を操作しないでください。
 *
 * @param handler ハンドラ
 * @return true/false (処理成功/処理失敗[ロック獲得失敗])
 */
bool kc_memory_entries(KcMemoryHandler handler)
{
	kc_memory_init_entry();

	// KcMemoryEntry のデータに handler を設定
	size_t         size  = sizeof(KcMemoryHandler);
	KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING);
	entry->data = handler;

	bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL);
	return is_executed;
}


/**
 * kc_memory_entries で使用されるハンドラ。
 *
 * @param entry エントリ(使用しない)
 * @param msg   メッセージ(使用しない)
 * @return true(固定)
 */
static
bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg)
{
	KcMemoryHandler handler    = (KcMemoryHandler) entry->data;
	KcMemoryEntry*  next_entry = kc_memory_head._next;
	while (next_entry != &kc_memory_tail)
	{
		handler(next_entry, msg);
		next_entry = next_entry->_next;
	}
	return true;
}


// freeif
/**
 * 指定された handler に現在管理しているメモリエントリが順次渡されます。
 * handler にて、true を返したメモリが解放されます。
 *
 * @param handler ハンドラ
 * @return true/false (処理成功/処理失敗[ロック獲得失敗])
 */
bool kc_memory_freeif(KcMemoryHandler handler)
{
	kc_memory_init_entry();

	// KcMemoryEntry のデータに handler を設定
	size_t         size  = sizeof(KcMemoryHandler);
	KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING);
	entry->data = handler;

	bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL);
	return is_executed;
}


/**
 * kc_memory_freeif で使用されるハンドラ。
 *
 * @param entry エントリ(使用しない)
 * @param msg   メッセージ(使用しない)
 * @return true(固定)
 */
static
bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg)
{
	KcMemoryHandler handler    = (KcMemoryHandler) entry->data;
	KcMemoryEntry*  next_entry = kc_memory_head._next;
	while (next_entry != &kc_memory_tail)
	{
		bool exec_free = handler(next_entry, msg);
		next_entry = next_entry->_next;
		if (exec_free)
		{	// メモリ解放
			kc_memory_free(next_entry->_prev->data);
		}
	}
	return true;
}


/**
 * 現在管理しているメモリ情報をダンプします。
 */
void kc_memory_dump(void)
{
	(void) kc_memory_entries(kc_memory_dump_entry);
}


/**
 * 指定されたメモリエントリの情報をダンプ出力します。
 *
 * @param entry 出力するエントリ
 * @param msg   メッセージ
 * @return true(固定)
 */
static
bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg)
{
	UNUSED_VARIABLE(msg);

	printf("%-15s:%05d:%-15s (%5d) %s ",
			entry->file,
			entry->line,
			entry->func,
			entry->size,
			kc_memory_strmark(entry->_mark));

	// dump
	kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE);

	// ascii
	printf(" | ");
	kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE);

	printf("\n");

	return true;
}




////////////////////////////////////////////////////////////////////////////////
//
//  内部関数群
//


// =============================================================================
//  メモリ確保解放
// =============================================================================


/**
 * 指定されたサイズのメモリを確保します。
 *
 * @param size  確保するメモリサイズ
 * @param mark  メモリ確保情報を示すマーク
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static
void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING);
	if (entry == NULL)
	{	// メモリ確保失敗
		kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line);
		kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate");
		return NULL;
	}

	kc_memory_set_entry(entry, size, mark, file, func, line);
	kc_memory_add_entry(entry);
	return (entry->data);
}


/**
 * 指定されたポインタがさすメモリサイズを変更します。
 * ポインタ ptr が NULL の場合、kc_memory_allocate を呼び出します。
 *
 * @param ptr   メモリサイズを変更するメモリへのポインタ
 * @param size  確保するメモリサイズ
 * @param mark  メモリ確保情報を示すマーク
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static
void* kc_memory_reallocate(void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	if (ptr == NULL)
	{
		return kc_memory_allocate(size, mark, file, func, line);
	}

	KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr;
	old_entry--;
	switch (old_entry->_mark)
	{
		case KC_MEMORY_ALLOCATED:			// 管理されたメモリの realloc
			return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line);
		case KC_MEMORY_ALLOCATED_NEW:		// 不正 (new で確保されたメモリへの realloc)
			return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line);
		case KC_MEMORY_ALLOCATED_NEW_ARRAY:	// 不正 (new[] で確保されたメモリへの realloc)
			return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line);
		case KC_MEMORY_DELETED:				// 削除済みメモリ => 通常の allocate と同様とする
			return kc_memory_allocate(size, mark, file, func, line);
		default:							// 管理外メモリの realloc
			return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line);
	}
}


/**
 * 管理されたメモリ領域に対する realloc を実施します。
 *
 * @param ptr   メモリサイズを変更するメモリへのポインタ (使用しない)
 * @param size  確保するメモリサイズ
 * @param mark  メモリ確保情報を示すマーク
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static
void* kc_memory_reallocate_managed_ptr(
	   void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	UNUSED_VARIABLE(ptr);

	KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr;
	old_entry--;

	kc_memory_remove_entry(old_entry);
	KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING);
	if (entry != NULL)
	{	// メモリ確保成功
		// メモリ管理リストに加えてポインタを返す。
		kc_memory_set_entry(entry, size, mark, file, func, line);
		kc_memory_add_entry(entry);
		return (entry->data);
	}
	else
	{	// メモリ確保失敗
		// エラーハンドラを実行し、NULL を返す。
		kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line);
		kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate");

		// 古いメモリ領域は残っているため、管理対象に戻す。
		kc_memory_add_entry(old_entry);
		return NULL;
	}
}


/**
 * 管理外メモリ領域に対する realloc を実施します。
 *
 * @param ptr   メモリサイズを変更するメモリへのポインタ (使用しない)
 * @param size  確保するメモリサイズ
 * @param mark  メモリ確保情報を示すマーク
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static
void* kc_memory_reallocate_unmanaged_ptr(
	   void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	// |<-- 新たな領域 ---------------->|
	// +------------+-------------------+
	// | 元々の領域 | 追加分 + 管理領域 |
	// +------------+-------------------+
	//   ↓
	//   ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー
	//   ↓
	// +----------+------------+--------+
	// | 管理領域 | 元々の領域 | 追加分 |
	// +----------+------------+--------+
	KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING);
	if (entry != NULL)
	{	// メモリ確保成功
		// memmove で元々の領域+追加分を、管理領域分を確保した先にコピー
		// メモリ管理リストに加えてポインタを返す。
		memmove((entry + 1), entry, size);
		kc_memory_set_entry(entry, size, mark, file, func, line);
		kc_memory_add_entry(entry);
		return (entry->data);
	}
	else
	{	// メモリ確保失敗
		// エラーハンドラを実行し、NULL を返す。
		kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line);
		kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate");
		return NULL;
	}
}


/**
 * 不正なメモリ領域に対する realloc のエラー処理を実施します。
 *
 * @param ptr   メモリサイズを変更するメモリへのポインタ (使用しない)
 * @param size  確保するメモリサイズ
 * @param mark  メモリ確保情報を示すマーク
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static
void* kc_memory_reallocate_invalid_ptr(
	   void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	UNUSED_VARIABLE(ptr);

	errno = EINVAL;
	kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line);
	kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)");
	return NULL;
}


/**
 * 指定されたポインタの指すメモリ領域を解放します。
 * NULL が指定された場合なにもしません。
 * 管理されたメモリの場合、管理領域を合わせて解放します。
 * 管理外メモリの場合、free を実行します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
static
void  kc_memory_deallocate(void* ptr)
{
	if (ptr == NULL)
	{	// NULL ポインタに対してはなにもしない
		return;
	}

	KcMemoryEntry* entry = (KcMemoryEntry*) ptr;
	entry--;
	switch (entry->_mark)
	{
		case KC_MEMORY_ALLOCATED:					// 管理メモリ
			kc_memory_deallocate_entry(entry);
			break;
		case KC_MEMORY_ALLOCATED_NEW:				// new により確保されたメモリ
			kc_memory_deallocate_entry(entry);
			perror("kc memory : warning : please use delete");
			kc_memory_execute_ehandler(entry, "warning : please use delete");
			break;
		case KC_MEMORY_ALLOCATED_NEW_ARRAY:			// new[] により確保されたメモリ
			kc_memory_deallocate_entry(entry);
			perror("kc memory : warning : please use delete[]");
			kc_memory_execute_ehandler(entry, "warning : please use delete[]");
			break;
		case KC_MEMORY_DELETED:						// 削除済みメモリ
			// Nothing to do.
			break;
		default:
			free(ptr);
			break;
	}
}


/**
 * 指定されたメモリ管理およびデータ領域を解放します。
 *
 * @param entry 解放するメモリ管理領域へのポインタ
 */
static
void kc_memory_deallocate_entry(KcMemoryEntry* entry)
{
	(void) kc_memory_remove_entry(entry);
	entry->_mark = KC_MEMORY_DELETED;
	entry->size  = 0;
	free(entry);
}


// =============================================================================
//  メモリエントリ管理
// =============================================================================


/**
 * メモリ管理エントリ全体を初期化します。
 */
static
void kc_memory_init_entry(void)
{
	static bool kc_memory_entry_initialized = false;
	if (!kc_memory_entry_initialized)
	{
		// メモリ管理エントリ初期化
		kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0);
		kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0);
		kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0);
		kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail;
		kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head;

		kc_memory_entry_initialized = true;
	}
}


/**
 * 指定された entry を指定された各パラメータで初期化します。
 *
 * @param entry  初期化するエントリ
 * @param size  確保するメモリサイズ
 * @param mark  メモリ確保情報を示すマーク
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 */ 
static
void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	entry->file  = file;
	entry->func  = func;
	entry->line  = line;
	entry->size  = size;
	entry->_mark = mark;
	entry->data  = (entry + 1);
	entry->_prev = NULL;
	entry->_next = NULL;
}


/**
 * 指定されたエントリをメモリ管理のリストに追加します。
 * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。
 *
 * @param entry 追加するエントリ
 */ 
static
void kc_memory_add_entry(KcMemoryEntry* entry)
{
	kc_memory_init_entry();
	bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL);
	if (is_executed)
	{
		kc_memory_execute_ahandler(entry, "allocate memory");
	}
	else
	{
		perror("kc memory : can't add entry");
		kc_memory_execute_ehandler(entry, "can't add entry");
	}
}


/**
 * 指定されたエントリをメモリ管理のリストに追加します。
 *
 * @param entry 追加するエントリ
 * @param msg   メッセージ (使用されません)
 * @return true (固定)
 */
static
bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg)
{
	UNUSED_VARIABLE(msg);

	// [tail] の一つ前に挿入する。
	entry->_next                = &kc_memory_tail;
	entry->_prev                = kc_memory_tail._prev;
	kc_memory_tail._prev->_next = entry;
	kc_memory_tail._prev        = entry;
	return true;
}


/**
 * 指定されたエントリをメモリ管理のリストから削除します。
 * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。
 *
 * @param entry 削除するエントリ
 */ 
static
void kc_memory_remove_entry(KcMemoryEntry* entry)
{
	kc_memory_init_entry();
	bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL);
	if (is_executed)
	{
		kc_memory_execute_fhandler(entry, "free memory");
	}
	else
	{
		perror("kc memory : can't remove entry");
		kc_memory_execute_ehandler(entry, "can't remove entry");
	}
}


/**
 * 指定されたエントリをメモリ管理のリストより削除します。
 *
 * @param entry 削除するエントリ
 * @param msg   メッセージ (使用されません)
 * @return true (固定)
 */
static
bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg)
{
	UNUSED_VARIABLE(msg);

	entry->_prev->_next         = entry->_next;
	entry->_next->_prev         = entry->_prev;
	return true;
}



// =============================================================================
//  ハンドラ実行
// =============================================================================


/**
 * メモリ確保時のハンドラを実行します。
 * ハンドラが未登録の場合何もしません。
 *
 * @param entry 確保したメモリエントリ
 * @param msg   メッセージ
 */
static
void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg)
{
	if (kc_memory_ahandler != NULL)
	{
		kc_memory_ahandler(entry, msg);
	}
}


/**
 * メモリ解放時のハンドラを実行します。
 * ハンドラが未登録の場合何もしません。
 *
 * @param entry 解放するメモリエントリ
 * @param msg   メッセージ
 */
static
void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg)
{
	if (kc_memory_fhandler != NULL)
	{
		kc_memory_fhandler(entry, msg);
	}
}


/**
 * エラー発生時のハンドラを実行します。
 * ハンドラが未登録の場合何もしません。
 *
 * @param entry 関連するメモリエントリ
 * @param msg   メッセージ
 */
static
void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg)
{
	if (kc_memory_ehandler != NULL)
	{
		kc_memory_ehandler(entry, msg);
	}
}


// ============================================================================
//  同期実行
// =============================================================================


/**
 * 同期化実現のための mutex を初期化します。
 *
 * @return true/false (初期化成功/失敗)
 */
static
bool kc_memory_mutex_init(void)
{
	static bool kc_memory_mutex_initialized = false;
	if (!kc_memory_mutex_initialized)
	{	// 未初期化の場合のみ実施する。
		// kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。
		// mtx_recursive を使用し、同関数内での再帰呼び出し
		// (handler内でのkc_memory_locked_execute)を許容する。
		int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive);
		if (result == thrd_success)
		{
			kc_memory_mutex_initialized = true;
		}
		else
		{
			perror("kc memory : can't init mutex");
			kc_memory_execute_ehandler(NULL, "can't init mutex");
		}
	}
	return kc_memory_mutex_initialized;
}


/**
 * 指定された handler の実行を同期化します。
 * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。
 *
 * @param handler 同期化して実行する関数
 * @param entry   handler へ渡される第一引数 (操作するメモリエントリ)
 * @param msg     handler へ渡される第二引数
 * @return true/false (handler を実行した/handler を実行できなかった)
 */
static
bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg)
{
	(void) kc_memory_mutex_init();

	int is_locked = mtx_lock(&kc_memory_mutex);
	if (is_locked == thrd_success)
	{
		// ハンドラの戻り値は Don't Care
		(void) handler(entry, msg);

		bool is_unlocked = mtx_unlock(&kc_memory_mutex);
		if (is_unlocked != thrd_success)
		{
			perror("memory : can't unlock");
			kc_memory_execute_ehandler(NULL, "can't unlock");
		}
		// アンロックに失敗しても handler 自体は実行しているため true を返す。
		return true;
	}
	return false;
}



// ============================================================================
//  データダンプ
// =============================================================================

/**
 * 指定されたバイトを ASCII 文字に変換します。
 *
 * @param c バイト
 */
#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.')


/**
 * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。
 *
 * @param mark 確保メモリ状態
 * @return 確保メモリ状態に対応する文字列表現
 */
static
const char* kc_memory_strmark(int mark)
{
	switch (mark)
	{
		case KC_MEMORY_DELETED:				return "del  ";
		case KC_MEMORY_ALLOCATED:			return "alloc";
		case KC_MEMORY_ALLOCATED_NEW:		return "new  ";
		case KC_MEMORY_ALLOCATED_NEW_ARRAY:	return "new[]";
		default:							return "other";
	}
}


/**
 * 指定されたメモリエントリのデータをダンプします。
 *
 * @param entry エントリ
 * @param dump_size ダンプするサイズ(バイト数)
 */
static
void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size)
{
	const unsigned char* data_ptr = (const unsigned char*) entry->data;
	int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size;
	int idx = 0;

	for (; idx < data_len; idx++)
	{
		printf("%02X ", data_ptr[idx]);
	}

	for (; idx < dump_size; idx++)
	{
		printf("-- ");
	}
}


/**
 * 指定されたメモリエントリのデータを ASCII 形式でダンプします。
 *
 * @param entry エントリ
 * @param dump_size ダンプするサイズ(バイト数)
 */
static
void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size)
{
	const unsigned char* data_ptr = (const unsigned char*) entry->data;
	int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size;
	int idx = 0;

	for (; idx < data_len; idx++)
	{
		printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx]));
	}

	for (; idx < dump_size; idx++)
	{
		printf(" ");
	}
}