/* * Copyright (c) 2008, 2009, Wayne Meissner * * All rights reserved. * * This file is part of ruby-ffi. * * This code is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 3 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * version 3 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with this work. If not, see . */ #ifndef _MSC_VER #include #endif #include #include #ifndef _MSC_VER #include #include #else typedef int bool; #define true 1 #define false 0 #endif #include #include #include "LastError.h" #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) && !defined(__WIN32__) # include # define USE_PTHREAD_LOCAL #endif typedef struct ThreadData { int td_errno; } ThreadData; #if defined(USE_PTHREAD_LOCAL) static pthread_key_t threadDataKey; #endif static inline ThreadData* thread_data_get(void); #if defined(USE_PTHREAD_LOCAL) static ThreadData* thread_data_init(void) { ThreadData* td = xcalloc(1, sizeof(ThreadData)); pthread_setspecific(threadDataKey, td); return td; } static inline ThreadData* thread_data_get(void) { ThreadData* td = pthread_getspecific(threadDataKey); return td != NULL ? td : thread_data_init(); } static void thread_data_free(void *ptr) { xfree(ptr); } #else static ID id_thread_data; static ThreadData* thread_data_init(void) { ThreadData* td; VALUE obj; obj = Data_Make_Struct(rb_cObject, ThreadData, NULL, -1, td); rb_thread_local_aset(rb_thread_current(), id_thread_data, obj); return td; } static inline ThreadData* thread_data_get() { VALUE obj = rb_thread_local_aref(rb_thread_current(), id_thread_data); if (obj != Qnil && TYPE(obj) == T_DATA) { return (ThreadData *) DATA_PTR(obj); } return thread_data_init(); } #endif /* * call-seq: error * @return [Numeric] * Get +errno+ value. */ static VALUE get_last_error(VALUE self) { return INT2NUM(thread_data_get()->td_errno); } /* * call-seq: error(error) * @param [Numeric] error * @return [nil] * Set +errno+ value. */ static VALUE set_last_error(VALUE self, VALUE error) { #ifdef _WIN32 SetLastError(NUM2INT(error)); #else errno = NUM2INT(error); #endif return Qnil; } void rbffi_save_errno(void) { int error = 0; #ifdef _WIN32 error = GetLastError(); #else error = errno; #endif thread_data_get()->td_errno = error; } void rbffi_LastError_Init(VALUE moduleFFI) { /* * Document-module: FFI::LastError * This module defines a couple of method to set and get +errno+ * for current thread. */ VALUE moduleError = rb_define_module_under(moduleFFI, "LastError"); rb_define_module_function(moduleError, "error", get_last_error, 0); rb_define_module_function(moduleError, "error=", set_last_error, 1); #if defined(USE_PTHREAD_LOCAL) pthread_key_create(&threadDataKey, thread_data_free); #else id_thread_data = rb_intern("ffi_thread_local_data"); #endif /* USE_PTHREAD_LOCAL */ }