nlib
misc/handlemaker/handlemaker.cpp

nn::nlib::HandleMakerを利用して、プロセス内で利用できる整数ハンドルを定義する方法を示します。

整数ハンドルでオブジェクトを間接的に参照する方法のメリットには以下のものがあります。

nn::nlib::HandleMakerクラスを利用すると、オブジェクトに対して整数ハンドルを割り当てるコードを簡単に記述することができます。

/*--------------------------------------------------------------------------------*
Project: CrossRoad
Copyright (C)Nintendo All rights reserved.
These coded instructions, statements, and computer programs contain proprietary
information of Nintendo and/or its licensed developers and are protected by
national and international copyright laws. They may not be disclosed to third
parties or copied or duplicated in any form, in whole or in part, without the
prior written consent of Nintendo.
The content herein is highly confidential and should be handled accordingly.
*--------------------------------------------------------------------------------*/
#include <memory>
class MyHandleBody {
public:
// These are used by HandleMaker
static const size_t N = 128; // The maximum number of MyHandleBody instances
void Lock() NLIB_NOEXCEPT NLIB_ACQUIRE(lock_) { nlib_mutex_lock(&lock_); }
void Unlock() NLIB_NOEXCEPT NLIB_RELEASE(lock_) { nlib_mutex_unlock(&lock_); }
bool IsHandleBodyEnabled() NLIB_NOEXCEPT { return valid_; }
public:
MyHandleBody() NLIB_NOEXCEPT {
valid_ = false;
access_count_ = 0;
nlib_mutex_init(&lock_);
}
~MyHandleBody() NLIB_NOEXCEPT {
NLIB_ASSERT(!valid_);
}
errno_t Initialize() NLIB_NOEXCEPT {
NLIB_ASSERT(!valid_);
// Initialize the object
valid_ = true;
return 0;
}
errno_t Finalize() NLIB_NOEXCEPT {
// lock_ is acquired.
if (!valid_) {
// if Finalize() called while waiting lock_ (invalid handle)
return EBADF;
}
// Finalize the object (close files etc.)
valid_ = false;
return 0; // The resource must be freed even in error case
}
errno_t access_method() NLIB_NOEXCEPT {
// lock_ is acquired.
if (!valid_) {
// if Finalize() called while waiting lock_ (invalid handle)
return EBADF;
}
// Use the resource exclusively.
++access_count_;
nlib_printf("myhandle_access() from thread %d : access_count = %d\n", id, access_count_);
return 0;
}
private:
bool valid_;
nlib_mutex lock_;
int access_count_;
// other data members ...
};
static nlib_ns::HandleTable<MyHandleBody> table_ = NLIB_HANDLETABLE_INITIALIZER;
// myhandle_open(), myhandle_close(), myhandle_access() are thread safe.
// you can call myhandle_open(), myhandle_close(), myhandle_access() with the closed(invalid) handle.
errno_t myhandle_open(int* handle) NLIB_NOEXCEPT {
std::unique_ptr<MyHandleBody> p(new (std::nothrow) MyHandleBody());
if (!p) return ENOMEM;
e = p->Initialize();
if (e != 0) {
return e;
}
e = maker.AttachHandleBody(handle, p.get());
if (nlib_is_error(e)) {
// no handle available
(void)p->Finalize();
return e; // ENFILE
}
p.release();
return 0;
}
errno_t myhandle_access(int handle) NLIB_NOEXCEPT {
e = maker.GetHandleAccess(handle, &access);
if (e != 0) return e; // EBADF
e = access->access_method();
return e;
}
errno_t myhandle_close(int handle) NLIB_NOEXCEPT {
e = maker.GetHandleAccess(handle, &access);
if (e != 0) return e; // EBADF
access.DetachHandleBody();
e = access->Finalize();
return e;
}
bool SampleHandleMaker() {
int h;
const size_t kNumThread = 8;
nlib_thread th[kNumThread];
e = myhandle_open(&h);
if (e != 0) return false;
auto thread_func = [](void* ph) {
int handle = (int)(intptr_t)ph;
errno_t err;
for (int j = 0; j < 3; ++j) {
// you can call myhandle_access() with the closed(invalid) handle (returns EBADF)
err = myhandle_access(handle);
if (err == EBADF) {
nlib_printf("myhandle_access() from thread %d, but handle already closed\n", id);
break;
}
}
// you can call myhandle_close() with the closed(invalid) handle (returns EBADF)
err = myhandle_close(handle);
nlib_printf("myhandle_close() from thread %d returns %s\n", id, nlib_error_string(err));
};
for (size_t i = 0; i < kNumThread; ++i) {
e = nlib_thread_create(&th[i], NULL, thread_func, (void*)(intptr_t)h);
NLIB_ASSERT(e == 0);
}
for (size_t i = 0; i < kNumThread; ++i) {
}
return true;
}
bool SampleMain(int, char**) { return SampleHandleMaker(); }
NLIB_MAINFUNC