| /* |
| * msvcrt.dll C++ objects |
| * |
| * Copyright 2017 Piotr Caban |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| |
| #include <stdarg.h> |
| |
| #include "windef.h" |
| #include "winternl.h" |
| #include "wine/debug.h" |
| #include "msvcrt.h" |
| #include "cppexcept.h" |
| #include "cxx.h" |
| |
| #if _MSVCR_VER >= 100 |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); |
| |
| static int context_id = -1; |
| static int scheduler_id = -1; |
| |
| #ifdef __i386__ |
| |
| #define DEFINE_VTBL_WRAPPER(off) \ |
| __ASM_GLOBAL_FUNC(vtbl_wrapper_ ## off, \ |
| "popl %eax\n\t" \ |
| "popl %ecx\n\t" \ |
| "pushl %eax\n\t" \ |
| "movl 0(%ecx), %eax\n\t" \ |
| "jmp *" #off "(%eax)\n\t") |
| |
| DEFINE_VTBL_WRAPPER(0); |
| DEFINE_VTBL_WRAPPER(4); |
| DEFINE_VTBL_WRAPPER(8); |
| DEFINE_VTBL_WRAPPER(12); |
| DEFINE_VTBL_WRAPPER(16); |
| DEFINE_VTBL_WRAPPER(20); |
| DEFINE_VTBL_WRAPPER(24); |
| DEFINE_VTBL_WRAPPER(28); |
| DEFINE_VTBL_WRAPPER(32); |
| DEFINE_VTBL_WRAPPER(36); |
| DEFINE_VTBL_WRAPPER(40); |
| DEFINE_VTBL_WRAPPER(44); |
| DEFINE_VTBL_WRAPPER(48); |
| |
| #endif |
| |
| typedef enum { |
| SchedulerKind, |
| MaxConcurrency, |
| MinConcurrency, |
| TargetOversubscriptionFactor, |
| LocalContextCacheSize, |
| ContextStackSize, |
| ContextPriority, |
| SchedulingProtocol, |
| DynamicProgressFeedback, |
| WinRTInitialization, |
| last_policy_id |
| } PolicyElementKey; |
| |
| typedef struct { |
| struct _policy_container { |
| unsigned int policies[last_policy_id]; |
| } *policy_container; |
| } SchedulerPolicy; |
| |
| typedef struct { |
| const vtable_ptr *vtable; |
| } Context; |
| #define call_Context_GetId(this) CALL_VTBL_FUNC(this, 0, \ |
| unsigned int, (const Context*), (this)) |
| #define call_Context_GetVirtualProcessorId(this) CALL_VTBL_FUNC(this, 4, \ |
| unsigned int, (const Context*), (this)) |
| #define call_Context_GetScheduleGroupId(this) CALL_VTBL_FUNC(this, 8, \ |
| unsigned int, (const Context*), (this)) |
| #define call_Context_dtor(this, flags) CALL_VTBL_FUNC(this, 20, \ |
| Context*, (Context*, unsigned int), (this, flags)) |
| |
| union allocator_cache_entry { |
| struct _free { |
| int depth; |
| union allocator_cache_entry *next; |
| } free; |
| struct _alloc { |
| int bucket; |
| char mem[1]; |
| } alloc; |
| }; |
| |
| struct scheduler_list { |
| struct Scheduler *scheduler; |
| struct scheduler_list *next; |
| }; |
| |
| typedef struct { |
| Context context; |
| struct scheduler_list scheduler; |
| unsigned int id; |
| union allocator_cache_entry *allocator_cache[8]; |
| } ExternalContextBase; |
| extern const vtable_ptr MSVCRT_ExternalContextBase_vtable; |
| static void ExternalContextBase_ctor(ExternalContextBase*); |
| |
| typedef struct Scheduler { |
| const vtable_ptr *vtable; |
| } Scheduler; |
| #define call_Scheduler_Id(this) CALL_VTBL_FUNC(this, 4, unsigned int, (const Scheduler*), (this)) |
| #define call_Scheduler_GetNumberOfVirtualProcessors(this) CALL_VTBL_FUNC(this, 8, unsigned int, (const Scheduler*), (this)) |
| #define call_Scheduler_GetPolicy(this,policy) CALL_VTBL_FUNC(this, 12, \ |
| SchedulerPolicy*, (Scheduler*,SchedulerPolicy*), (this,policy)) |
| #define call_Scheduler_Reference(this) CALL_VTBL_FUNC(this, 16, unsigned int, (Scheduler*), (this)) |
| #define call_Scheduler_Release(this) CALL_VTBL_FUNC(this, 20, unsigned int, (Scheduler*), (this)) |
| #define call_Scheduler_RegisterShutdownEvent(this,event) CALL_VTBL_FUNC(this, 24, void, (Scheduler*,HANDLE), (this,event)) |
| #define call_Scheduler_Attach(this) CALL_VTBL_FUNC(this, 28, void, (Scheduler*), (this)) |
| #if _MSVCR_VER > 100 |
| #define call_Scheduler_CreateScheduleGroup_loc(this,placement) CALL_VTBL_FUNC(this, 32, \ |
| /*ScheduleGroup*/void*, (Scheduler*,/*location*/void*), (this,placement)) |
| #define call_Scheduler_CreateScheduleGroup(this) CALL_VTBL_FUNC(this, 36, /*ScheduleGroup*/void*, (Scheduler*), (this)) |
| #define call_Scheduler_ScheduleTask_loc(this,proc,data,placement) CALL_VTBL_FUNC(this, 40, \ |
| void, (Scheduler*,void (__cdecl*)(void*),void*,/*location*/void*), (this,proc,data,placement)) |
| #define call_Scheduler_ScheduleTask(this,proc,data) CALL_VTBL_FUNC(this, 44, \ |
| void, (Scheduler*,void (__cdecl*)(void*),void*), (this,proc,data)) |
| #define call_Scheduler_IsAvailableLocation(this,placement) CALL_VTBL_FUNC(this, 48, \ |
| MSVCRT_bool, (Scheduler*,const /*location*/void*), (this,placement)) |
| #else |
| #define call_Scheduler_CreateScheduleGroup(this) CALL_VTBL_FUNC(this, 32, /*ScheduleGroup*/void*, (Scheduler*), (this)) |
| #define call_Scheduler_ScheduleTask(this,proc,data) CALL_VTBL_FUNC(this, 36, \ |
| void, (Scheduler*,void (__cdecl*)(void*),void*), (this,proc,data)) |
| #endif |
| |
| typedef struct { |
| Scheduler scheduler; |
| LONG ref; |
| unsigned int id; |
| unsigned int virt_proc_no; |
| SchedulerPolicy policy; |
| int shutdown_count; |
| int shutdown_size; |
| HANDLE *shutdown_events; |
| CRITICAL_SECTION cs; |
| } ThreadScheduler; |
| extern const vtable_ptr MSVCRT_ThreadScheduler_vtable; |
| |
| typedef struct { |
| Scheduler *scheduler; |
| } _Scheduler; |
| |
| typedef struct { |
| char empty; |
| } _CurrentScheduler; |
| |
| static int context_tls_index = TLS_OUT_OF_INDEXES; |
| |
| static CRITICAL_SECTION default_scheduler_cs; |
| static CRITICAL_SECTION_DEBUG default_scheduler_cs_debug = |
| { |
| 0, 0, &default_scheduler_cs, |
| { &default_scheduler_cs_debug.ProcessLocksList, &default_scheduler_cs_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": default_scheduler_cs") } |
| }; |
| static CRITICAL_SECTION default_scheduler_cs = { &default_scheduler_cs_debug, -1, 0, 0, 0, 0 }; |
| static SchedulerPolicy default_scheduler_policy; |
| static ThreadScheduler *default_scheduler; |
| |
| static void create_default_scheduler(void); |
| |
| static Context* try_get_current_context(void) |
| { |
| if (context_tls_index == TLS_OUT_OF_INDEXES) |
| return NULL; |
| return TlsGetValue(context_tls_index); |
| } |
| |
| static Context* get_current_context(void) |
| { |
| Context *ret; |
| |
| if (context_tls_index == TLS_OUT_OF_INDEXES) { |
| int tls_index = TlsAlloc(); |
| if (tls_index == TLS_OUT_OF_INDEXES) { |
| throw_exception(EXCEPTION_SCHEDULER_RESOURCE_ALLOCATION_ERROR, |
| HRESULT_FROM_WIN32(GetLastError()), NULL); |
| return NULL; |
| } |
| |
| if(InterlockedCompareExchange(&context_tls_index, tls_index, TLS_OUT_OF_INDEXES) != TLS_OUT_OF_INDEXES) |
| TlsFree(tls_index); |
| } |
| |
| ret = TlsGetValue(context_tls_index); |
| if (!ret) { |
| ExternalContextBase *context = MSVCRT_operator_new(sizeof(ExternalContextBase)); |
| ExternalContextBase_ctor(context); |
| TlsSetValue(context_tls_index, context); |
| ret = &context->context; |
| } |
| return ret; |
| } |
| |
| static Scheduler* try_get_current_scheduler(void) |
| { |
| ExternalContextBase *context = (ExternalContextBase*)try_get_current_context(); |
| |
| if (!context) |
| return NULL; |
| |
| if (context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { |
| ERR("unknown context set\n"); |
| return NULL; |
| } |
| return context->scheduler.scheduler; |
| } |
| |
| static Scheduler* get_current_scheduler(void) |
| { |
| ExternalContextBase *context = (ExternalContextBase*)get_current_context(); |
| |
| if (context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { |
| ERR("unknown context set\n"); |
| return NULL; |
| } |
| return context->scheduler.scheduler; |
| } |
| |
| /* ?CurrentContext@Context@Concurrency@@SAPAV12@XZ */ |
| /* ?CurrentContext@Context@Concurrency@@SAPEAV12@XZ */ |
| Context* __cdecl Context_CurrentContext(void) |
| { |
| TRACE("()\n"); |
| return get_current_context(); |
| } |
| |
| /* ?Id@Context@Concurrency@@SAIXZ */ |
| unsigned int __cdecl Context_Id(void) |
| { |
| Context *ctx = try_get_current_context(); |
| TRACE("()\n"); |
| return ctx ? call_Context_GetId(ctx) : -1; |
| } |
| |
| /* ?Block@Context@Concurrency@@SAXXZ */ |
| void __cdecl Context_Block(void) |
| { |
| FIXME("()\n"); |
| } |
| |
| /* ?Yield@Context@Concurrency@@SAXXZ */ |
| void __cdecl Context_Yield(void) |
| { |
| FIXME("()\n"); |
| } |
| |
| /* ?_SpinYield@Context@Concurrency@@SAXXZ */ |
| void __cdecl Context__SpinYield(void) |
| { |
| FIXME("()\n"); |
| } |
| |
| /* ?IsCurrentTaskCollectionCanceling@Context@Concurrency@@SA_NXZ */ |
| MSVCRT_bool __cdecl Context_IsCurrentTaskCollectionCanceling(void) |
| { |
| FIXME("()\n"); |
| return FALSE; |
| } |
| |
| /* ?Oversubscribe@Context@Concurrency@@SAX_N@Z */ |
| void __cdecl Context_Oversubscribe(MSVCRT_bool begin) |
| { |
| FIXME("(%x)\n", begin); |
| } |
| |
| /* ?ScheduleGroupId@Context@Concurrency@@SAIXZ */ |
| unsigned int __cdecl Context_ScheduleGroupId(void) |
| { |
| Context *ctx = try_get_current_context(); |
| TRACE("()\n"); |
| return ctx ? call_Context_GetScheduleGroupId(ctx) : -1; |
| } |
| |
| /* ?VirtualProcessorId@Context@Concurrency@@SAIXZ */ |
| unsigned int __cdecl Context_VirtualProcessorId(void) |
| { |
| Context *ctx = try_get_current_context(); |
| TRACE("()\n"); |
| return ctx ? call_Context_GetVirtualProcessorId(ctx) : -1; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetId, 4) |
| unsigned int __thiscall ExternalContextBase_GetId(const ExternalContextBase *this) |
| { |
| TRACE("(%p)->()\n", this); |
| return this->id; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetVirtualProcessorId, 4) |
| unsigned int __thiscall ExternalContextBase_GetVirtualProcessorId(const ExternalContextBase *this) |
| { |
| FIXME("(%p)->() stub\n", this); |
| return -1; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetScheduleGroupId, 4) |
| unsigned int __thiscall ExternalContextBase_GetScheduleGroupId(const ExternalContextBase *this) |
| { |
| FIXME("(%p)->() stub\n", this); |
| return -1; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ExternalContextBase_Unblock, 4) |
| void __thiscall ExternalContextBase_Unblock(ExternalContextBase *this) |
| { |
| FIXME("(%p)->() stub\n", this); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ExternalContextBase_IsSynchronouslyBlocked, 4) |
| MSVCRT_bool __thiscall ExternalContextBase_IsSynchronouslyBlocked(const ExternalContextBase *this) |
| { |
| FIXME("(%p)->() stub\n", this); |
| return FALSE; |
| } |
| |
| static void ExternalContextBase_dtor(ExternalContextBase *this) |
| { |
| struct scheduler_list *scheduler_cur, *scheduler_next; |
| union allocator_cache_entry *next, *cur; |
| int i; |
| |
| /* TODO: move the allocator cache to scheduler so it can be reused */ |
| for(i=0; i<sizeof(this->allocator_cache)/sizeof(this->allocator_cache[0]); i++) { |
| for(cur = this->allocator_cache[i]; cur; cur=next) { |
| next = cur->free.next; |
| MSVCRT_operator_delete(cur); |
| } |
| } |
| |
| if (this->scheduler.scheduler) { |
| call_Scheduler_Release(this->scheduler.scheduler); |
| |
| for(scheduler_cur=this->scheduler.next; scheduler_cur; scheduler_cur=scheduler_next) { |
| scheduler_next = scheduler_cur->next; |
| call_Scheduler_Release(scheduler_cur->scheduler); |
| MSVCRT_operator_delete(scheduler_cur); |
| } |
| } |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ExternalContextBase_vector_dtor, 8) |
| Context* __thiscall ExternalContextBase_vector_dtor(ExternalContextBase *this, unsigned int flags) |
| { |
| TRACE("(%p %x)\n", this, flags); |
| if(flags & 2) { |
| /* we have an array, with the number of elements stored before the first object */ |
| INT_PTR i, *ptr = (INT_PTR *)this-1; |
| |
| for(i=*ptr-1; i>=0; i--) |
| ExternalContextBase_dtor(this+i); |
| MSVCRT_operator_delete(ptr); |
| } else { |
| ExternalContextBase_dtor(this); |
| if(flags & 1) |
| MSVCRT_operator_delete(this); |
| } |
| |
| return &this->context; |
| } |
| |
| static void ExternalContextBase_ctor(ExternalContextBase *this) |
| { |
| TRACE("(%p)->()\n", this); |
| |
| memset(this, 0, sizeof(*this)); |
| this->context.vtable = &MSVCRT_ExternalContextBase_vtable; |
| this->id = InterlockedIncrement(&context_id); |
| |
| create_default_scheduler(); |
| this->scheduler.scheduler = &default_scheduler->scheduler; |
| call_Scheduler_Reference(&default_scheduler->scheduler); |
| } |
| |
| /* ?Alloc@Concurrency@@YAPAXI@Z */ |
| /* ?Alloc@Concurrency@@YAPEAX_K@Z */ |
| void * CDECL Concurrency_Alloc(MSVCRT_size_t size) |
| { |
| ExternalContextBase *context = (ExternalContextBase*)get_current_context(); |
| union allocator_cache_entry *p; |
| |
| size += FIELD_OFFSET(union allocator_cache_entry, alloc.mem); |
| if (size < sizeof(*p)) |
| size = sizeof(*p); |
| |
| if (context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { |
| p = MSVCRT_operator_new(size); |
| p->alloc.bucket = -1; |
| }else { |
| int i; |
| |
| C_ASSERT(sizeof(union allocator_cache_entry) <= 1 << 4); |
| for(i=0; i<sizeof(context->allocator_cache)/sizeof(context->allocator_cache[0]); i++) |
| if (1 << (i+4) >= size) break; |
| |
| if(i==sizeof(context->allocator_cache)/sizeof(context->allocator_cache[0])) { |
| p = MSVCRT_operator_new(size); |
| p->alloc.bucket = -1; |
| }else if (context->allocator_cache[i]) { |
| p = context->allocator_cache[i]; |
| context->allocator_cache[i] = p->free.next; |
| p->alloc.bucket = i; |
| }else { |
| p = MSVCRT_operator_new(1 << (i+4)); |
| p->alloc.bucket = i; |
| } |
| } |
| |
| TRACE("(%ld) returning %p\n", size, p->alloc.mem); |
| return p->alloc.mem; |
| } |
| |
| /* ?Free@Concurrency@@YAXPAX@Z */ |
| /* ?Free@Concurrency@@YAXPEAX@Z */ |
| void CDECL Concurrency_Free(void* mem) |
| { |
| union allocator_cache_entry *p = (union allocator_cache_entry*)((char*)mem-FIELD_OFFSET(union allocator_cache_entry, alloc.mem)); |
| ExternalContextBase *context = (ExternalContextBase*)get_current_context(); |
| int bucket = p->alloc.bucket; |
| |
| TRACE("(%p)\n", mem); |
| |
| if (context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { |
| MSVCRT_operator_delete(p); |
| }else { |
| if(bucket >= 0 && bucket < sizeof(context->allocator_cache)/sizeof(context->allocator_cache[0]) && |
| (!context->allocator_cache[bucket] || context->allocator_cache[bucket]->free.depth < 20)) { |
| p->free.next = context->allocator_cache[bucket]; |
| p->free.depth = p->free.next ? p->free.next->free.depth+1 : 0; |
| context->allocator_cache[bucket] = p; |
| }else { |
| MSVCRT_operator_delete(p); |
| } |
| } |
| } |
| |
| /* ?SetPolicyValue@SchedulerPolicy@Concurrency@@QAEIW4PolicyElementKey@2@I@Z */ |
| /* ?SetPolicyValue@SchedulerPolicy@Concurrency@@QEAAIW4PolicyElementKey@2@I@Z */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_SetPolicyValue, 12) |
| unsigned int __thiscall SchedulerPolicy_SetPolicyValue(SchedulerPolicy *this, |
| PolicyElementKey policy, unsigned int val) |
| { |
| unsigned int ret; |
| |
| TRACE("(%p %d %d)\n", this, policy, val); |
| |
| if (policy == MinConcurrency) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_KEY, 0, "MinConcurrency"); |
| if (policy == MaxConcurrency) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_KEY, 0, "MaxConcurrency"); |
| if (policy >= last_policy_id) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_KEY, 0, "Invalid policy"); |
| |
| switch(policy) { |
| case SchedulerKind: |
| if (val) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_VALUE, 0, "SchedulerKind"); |
| break; |
| case TargetOversubscriptionFactor: |
| if (!val) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_VALUE, |
| 0, "TargetOversubscriptionFactor"); |
| break; |
| case ContextPriority: |
| if (((int)val < -7 /* THREAD_PRIORITY_REALTIME_LOWEST */ |
| || val > 6 /* THREAD_PRIORITY_REALTIME_HIGHEST */) |
| && val != THREAD_PRIORITY_IDLE && val != THREAD_PRIORITY_TIME_CRITICAL |
| && val != INHERIT_THREAD_PRIORITY) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_VALUE, 0, "ContextPriority"); |
| break; |
| case SchedulingProtocol: |
| case DynamicProgressFeedback: |
| case WinRTInitialization: |
| if (val != 0 && val != 1) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_VALUE, 0, "SchedulingProtocol"); |
| break; |
| default: |
| break; |
| } |
| |
| ret = this->policy_container->policies[policy]; |
| this->policy_container->policies[policy] = val; |
| return ret; |
| } |
| |
| /* ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QAEXII@Z */ |
| /* ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QEAAXII@Z */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_SetConcurrencyLimits, 12) |
| void __thiscall SchedulerPolicy_SetConcurrencyLimits(SchedulerPolicy *this, |
| unsigned int min_concurrency, unsigned int max_concurrency) |
| { |
| TRACE("(%p %d %d)\n", this, min_concurrency, max_concurrency); |
| |
| if (min_concurrency > max_concurrency) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_THREAD_SPECIFICATION, 0, NULL); |
| if (!max_concurrency) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_VALUE, 0, "MaxConcurrency"); |
| |
| this->policy_container->policies[MinConcurrency] = min_concurrency; |
| this->policy_container->policies[MaxConcurrency] = max_concurrency; |
| } |
| |
| /* ?GetPolicyValue@SchedulerPolicy@Concurrency@@QBEIW4PolicyElementKey@2@@Z */ |
| /* ?GetPolicyValue@SchedulerPolicy@Concurrency@@QEBAIW4PolicyElementKey@2@@Z */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_GetPolicyValue, 8) |
| unsigned int __thiscall SchedulerPolicy_GetPolicyValue( |
| const SchedulerPolicy *this, PolicyElementKey policy) |
| { |
| TRACE("(%p %d)\n", this, policy); |
| |
| if (policy >= last_policy_id) |
| throw_exception(EXCEPTION_INVALID_SCHEDULER_POLICY_KEY, 0, "Invalid policy"); |
| return this->policy_container->policies[policy]; |
| } |
| |
| /* ??0SchedulerPolicy@Concurrency@@QAE@XZ */ |
| /* ??0SchedulerPolicy@Concurrency@@QEAA@XZ */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_ctor, 4) |
| SchedulerPolicy* __thiscall SchedulerPolicy_ctor(SchedulerPolicy *this) |
| { |
| TRACE("(%p)\n", this); |
| |
| this->policy_container = MSVCRT_operator_new(sizeof(*this->policy_container)); |
| /* TODO: default values can probably be affected by CurrentScheduler */ |
| this->policy_container->policies[SchedulerKind] = 0; |
| this->policy_container->policies[MaxConcurrency] = -1; |
| this->policy_container->policies[MinConcurrency] = 1; |
| this->policy_container->policies[TargetOversubscriptionFactor] = 1; |
| this->policy_container->policies[LocalContextCacheSize] = 8; |
| this->policy_container->policies[ContextStackSize] = 0; |
| this->policy_container->policies[ContextPriority] = THREAD_PRIORITY_NORMAL; |
| this->policy_container->policies[SchedulingProtocol] = 0; |
| this->policy_container->policies[DynamicProgressFeedback] = 1; |
| return this; |
| } |
| |
| /* ??0SchedulerPolicy@Concurrency@@QAA@IZZ */ |
| /* ??0SchedulerPolicy@Concurrency@@QEAA@_KZZ */ |
| /* TODO: don't leak policy_container on exception */ |
| SchedulerPolicy* __cdecl SchedulerPolicy_ctor_policies( |
| SchedulerPolicy *this, MSVCRT_size_t n, ...) |
| { |
| unsigned int min_concurrency, max_concurrency; |
| __ms_va_list valist; |
| MSVCRT_size_t i; |
| |
| TRACE("(%p %ld)\n", this, n); |
| |
| SchedulerPolicy_ctor(this); |
| min_concurrency = this->policy_container->policies[MinConcurrency]; |
| max_concurrency = this->policy_container->policies[MaxConcurrency]; |
| |
| __ms_va_start(valist, n); |
| for(i=0; i<n; i++) { |
| PolicyElementKey policy = va_arg(valist, PolicyElementKey); |
| unsigned int val = va_arg(valist, unsigned int); |
| |
| if(policy == MinConcurrency) |
| min_concurrency = val; |
| else if(policy == MaxConcurrency) |
| max_concurrency = val; |
| else |
| SchedulerPolicy_SetPolicyValue(this, policy, val); |
| } |
| __ms_va_end(valist); |
| |
| SchedulerPolicy_SetConcurrencyLimits(this, min_concurrency, max_concurrency); |
| return this; |
| } |
| |
| /* ??4SchedulerPolicy@Concurrency@@QAEAAV01@ABV01@@Z */ |
| /* ??4SchedulerPolicy@Concurrency@@QEAAAEAV01@AEBV01@@Z */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_op_assign, 8) |
| SchedulerPolicy* __thiscall SchedulerPolicy_op_assign( |
| SchedulerPolicy *this, const SchedulerPolicy *rhs) |
| { |
| TRACE("(%p %p)\n", this, rhs); |
| memcpy(this->policy_container->policies, rhs->policy_container->policies, |
| sizeof(this->policy_container->policies)); |
| return this; |
| } |
| |
| /* ??0SchedulerPolicy@Concurrency@@QAE@ABV01@@Z */ |
| /* ??0SchedulerPolicy@Concurrency@@QEAA@AEBV01@@Z */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_copy_ctor, 8) |
| SchedulerPolicy* __thiscall SchedulerPolicy_copy_ctor( |
| SchedulerPolicy *this, const SchedulerPolicy *rhs) |
| { |
| TRACE("(%p %p)\n", this, rhs); |
| SchedulerPolicy_ctor(this); |
| return SchedulerPolicy_op_assign(this, rhs); |
| } |
| |
| /* ??1SchedulerPolicy@Concurrency@@QAE@XZ */ |
| /* ??1SchedulerPolicy@Concurrency@@QEAA@XZ */ |
| DEFINE_THISCALL_WRAPPER(SchedulerPolicy_dtor, 4) |
| void __thiscall SchedulerPolicy_dtor(SchedulerPolicy *this) |
| { |
| TRACE("(%p)\n", this); |
| MSVCRT_operator_delete(this->policy_container); |
| } |
| |
| static void ThreadScheduler_dtor(ThreadScheduler *this) |
| { |
| int i; |
| |
| if(this->ref != 0) WARN("ref = %d\n", this->ref); |
| SchedulerPolicy_dtor(&this->policy); |
| |
| for(i=0; i<this->shutdown_count; i++) |
| SetEvent(this->shutdown_events[i]); |
| MSVCRT_operator_delete(this->shutdown_events); |
| |
| this->cs.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&this->cs); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_Id, 4) |
| unsigned int __thiscall ThreadScheduler_Id(const ThreadScheduler *this) |
| { |
| TRACE("(%p)\n", this); |
| return this->id; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_GetNumberOfVirtualProcessors, 4) |
| unsigned int __thiscall ThreadScheduler_GetNumberOfVirtualProcessors(const ThreadScheduler *this) |
| { |
| TRACE("(%p)\n", this); |
| return this->virt_proc_no; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_GetPolicy, 8) |
| SchedulerPolicy* __thiscall ThreadScheduler_GetPolicy( |
| const ThreadScheduler *this, SchedulerPolicy *ret) |
| { |
| TRACE("(%p %p)\n", this, ret); |
| return SchedulerPolicy_copy_ctor(ret, &this->policy); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_Reference, 4) |
| unsigned int __thiscall ThreadScheduler_Reference(ThreadScheduler *this) |
| { |
| TRACE("(%p)\n", this); |
| return InterlockedIncrement(&this->ref); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_Release, 4) |
| unsigned int __thiscall ThreadScheduler_Release(ThreadScheduler *this) |
| { |
| unsigned int ret = InterlockedDecrement(&this->ref); |
| |
| TRACE("(%p)\n", this); |
| |
| if(!ret) { |
| ThreadScheduler_dtor(this); |
| MSVCRT_operator_delete(this); |
| } |
| return ret; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_RegisterShutdownEvent, 8) |
| void __thiscall ThreadScheduler_RegisterShutdownEvent(ThreadScheduler *this, HANDLE event) |
| { |
| HANDLE *shutdown_events; |
| int size; |
| |
| TRACE("(%p %p)\n", this, event); |
| |
| EnterCriticalSection(&this->cs); |
| |
| size = this->shutdown_size ? this->shutdown_size * 2 : 1; |
| shutdown_events = MSVCRT_operator_new(size * sizeof(*shutdown_events)); |
| memcpy(shutdown_events, this->shutdown_events, |
| this->shutdown_count * sizeof(*shutdown_events)); |
| MSVCRT_operator_delete(this->shutdown_events); |
| this->shutdown_size = size; |
| this->shutdown_events = shutdown_events; |
| this->shutdown_events[this->shutdown_count++] = event; |
| |
| LeaveCriticalSection(&this->cs); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_Attach, 4) |
| void __thiscall ThreadScheduler_Attach(ThreadScheduler *this) |
| { |
| ExternalContextBase *context = (ExternalContextBase*)get_current_context(); |
| |
| TRACE("(%p)\n", this); |
| |
| if(context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { |
| ERR("unknown context set\n"); |
| return; |
| } |
| |
| if(context->scheduler.scheduler == &this->scheduler) |
| throw_exception(EXCEPTION_IMPROPER_SCHEDULER_ATTACH, 0, NULL); |
| |
| if(context->scheduler.scheduler) { |
| struct scheduler_list *l = MSVCRT_operator_new(sizeof(*l)); |
| *l = context->scheduler; |
| context->scheduler.next = l; |
| } |
| context->scheduler.scheduler = &this->scheduler; |
| ThreadScheduler_Reference(this); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_CreateScheduleGroup_loc, 8) |
| /*ScheduleGroup*/void* __thiscall ThreadScheduler_CreateScheduleGroup_loc( |
| ThreadScheduler *this, /*location*/void *placement) |
| { |
| FIXME("(%p %p) stub\n", this, placement); |
| return NULL; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_CreateScheduleGroup, 4) |
| /*ScheduleGroup*/void* __thiscall ThreadScheduler_CreateScheduleGroup(ThreadScheduler *this) |
| { |
| FIXME("(%p) stub\n", this); |
| return NULL; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_ScheduleTask_loc, 16) |
| void __thiscall ThreadScheduler_ScheduleTask_loc(ThreadScheduler *this, |
| void (__cdecl *proc)(void*), void* data, /*location*/void *placement) |
| { |
| FIXME("(%p %p %p %p) stub\n", this, proc, data, placement); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_ScheduleTask, 12) |
| void __thiscall ThreadScheduler_ScheduleTask(ThreadScheduler *this, |
| void (__cdecl *proc)(void*), void* data) |
| { |
| FIXME("(%p %p %p) stub\n", this, proc, data); |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_IsAvailableLocation, 8) |
| MSVCRT_bool __thiscall ThreadScheduler_IsAvailableLocation( |
| const ThreadScheduler *this, const /*location*/void *placement) |
| { |
| FIXME("(%p %p) stub\n", this, placement); |
| return FALSE; |
| } |
| |
| DEFINE_THISCALL_WRAPPER(ThreadScheduler_vector_dtor, 8) |
| Scheduler* __thiscall ThreadScheduler_vector_dtor(ThreadScheduler *this, unsigned int flags) |
| { |
| TRACE("(%p %x)\n", this, flags); |
| if(flags & 2) { |
| /* we have an array, with the number of elements stored before the first object */ |
| INT_PTR i, *ptr = (INT_PTR *)this-1; |
| |
| for(i=*ptr-1; i>=0; i--) |
| ThreadScheduler_dtor(this+i); |
| MSVCRT_operator_delete(ptr); |
| } else { |
| ThreadScheduler_dtor(this); |
| if(flags & 1) |
| MSVCRT_operator_delete(this); |
| } |
| |
| return &this->scheduler; |
| } |
| |
| static ThreadScheduler* ThreadScheduler_ctor(ThreadScheduler *this, |
| const SchedulerPolicy *policy) |
| { |
| SYSTEM_INFO si; |
| |
| TRACE("(%p)->()\n", this); |
| |
| this->scheduler.vtable = &MSVCRT_ThreadScheduler_vtable; |
| this->ref = 1; |
| this->id = InterlockedIncrement(&scheduler_id); |
| SchedulerPolicy_copy_ctor(&this->policy, policy); |
| |
| GetSystemInfo(&si); |
| this->virt_proc_no = SchedulerPolicy_GetPolicyValue(&this->policy, MaxConcurrency); |
| if(this->virt_proc_no > si.dwNumberOfProcessors) |
| this->virt_proc_no = si.dwNumberOfProcessors; |
| |
| this->shutdown_count = this->shutdown_size = 0; |
| this->shutdown_events = NULL; |
| |
| InitializeCriticalSection(&this->cs); |
| this->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ThreadScheduler"); |
| return this; |
| } |
| |
| /* ?Create@Scheduler@Concurrency@@SAPAV12@ABVSchedulerPolicy@2@@Z */ |
| /* ?Create@Scheduler@Concurrency@@SAPEAV12@AEBVSchedulerPolicy@2@@Z */ |
| Scheduler* __cdecl Scheduler_Create(const SchedulerPolicy *policy) |
| { |
| ThreadScheduler *ret; |
| |
| TRACE("(%p)\n", policy); |
| |
| ret = MSVCRT_operator_new(sizeof(*ret)); |
| return &ThreadScheduler_ctor(ret, policy)->scheduler; |
| } |
| |
| /* ?ResetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXXZ */ |
| void __cdecl Scheduler_ResetDefaultSchedulerPolicy(void) |
| { |
| TRACE("()\n"); |
| |
| EnterCriticalSection(&default_scheduler_cs); |
| if(default_scheduler_policy.policy_container) |
| SchedulerPolicy_dtor(&default_scheduler_policy); |
| SchedulerPolicy_ctor(&default_scheduler_policy); |
| LeaveCriticalSection(&default_scheduler_cs); |
| } |
| |
| /* ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z */ |
| /* ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXAEBVSchedulerPolicy@2@@Z */ |
| void __cdecl Scheduler_SetDefaultSchedulerPolicy(const SchedulerPolicy *policy) |
| { |
| TRACE("(%p)\n", policy); |
| |
| EnterCriticalSection(&default_scheduler_cs); |
| if(!default_scheduler_policy.policy_container) |
| SchedulerPolicy_copy_ctor(&default_scheduler_policy, policy); |
| else |
| SchedulerPolicy_op_assign(&default_scheduler_policy, policy); |
| LeaveCriticalSection(&default_scheduler_cs); |
| } |
| |
| /* ?Create@CurrentScheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z */ |
| /* ?Create@CurrentScheduler@Concurrency@@SAXAEBVSchedulerPolicy@2@@Z */ |
| void __cdecl CurrentScheduler_Create(const SchedulerPolicy *policy) |
| { |
| Scheduler *scheduler; |
| |
| TRACE("(%p)\n", policy); |
| |
| scheduler = Scheduler_Create(policy); |
| call_Scheduler_Attach(scheduler); |
| } |
| |
| /* ?Detach@CurrentScheduler@Concurrency@@SAXXZ */ |
| void __cdecl CurrentScheduler_Detach(void) |
| { |
| ExternalContextBase *context = (ExternalContextBase*)try_get_current_context(); |
| |
| TRACE("()\n"); |
| |
| if(!context) |
| throw_exception(EXCEPTION_IMPROPER_SCHEDULER_DETACH, 0, NULL); |
| |
| if(context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { |
| ERR("unknown context set\n"); |
| return; |
| } |
| |
| if(!context->scheduler.next) |
| throw_exception(EXCEPTION_IMPROPER_SCHEDULER_DETACH, 0, NULL); |
| |
| call_Scheduler_Release(context->scheduler.scheduler); |
| if(!context->scheduler.next) { |
| context->scheduler.scheduler = NULL; |
| }else { |
| struct scheduler_list *entry = context->scheduler.next; |
| context->scheduler.scheduler = entry->scheduler; |
| context->scheduler.next = entry->next; |
| MSVCRT_operator_delete(entry); |
| } |
| } |
| |
| static void create_default_scheduler(void) |
| { |
| if(default_scheduler) |
| return; |
| |
| EnterCriticalSection(&default_scheduler_cs); |
| if(!default_scheduler) { |
| ThreadScheduler *scheduler; |
| |
| if(!default_scheduler_policy.policy_container) |
| SchedulerPolicy_ctor(&default_scheduler_policy); |
| |
| scheduler = MSVCRT_operator_new(sizeof(*scheduler)); |
| ThreadScheduler_ctor(scheduler, &default_scheduler_policy); |
| default_scheduler = scheduler; |
| } |
| LeaveCriticalSection(&default_scheduler_cs); |
| } |
| |
| /* ?Get@CurrentScheduler@Concurrency@@SAPAVScheduler@2@XZ */ |
| /* ?Get@CurrentScheduler@Concurrency@@SAPEAVScheduler@2@XZ */ |
| Scheduler* __cdecl CurrentScheduler_Get(void) |
| { |
| TRACE("()\n"); |
| return get_current_scheduler(); |
| } |
| |
| #if _MSVCR_VER > 100 |
| /* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@AAVlocation@2@@Z */ |
| /* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPEAVScheduleGroup@2@AEAVlocation@2@@Z */ |
| /*ScheduleGroup*/void* __cdecl CurrentScheduler_CreateScheduleGroup_loc(/*location*/void *placement) |
| { |
| TRACE("(%p)\n", placement); |
| return call_Scheduler_CreateScheduleGroup_loc(get_current_scheduler(), placement); |
| } |
| #endif |
| |
| /* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@XZ */ |
| /* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPEAVScheduleGroup@2@XZ */ |
| /*ScheduleGroup*/void* __cdecl CurrentScheduler_CreateScheduleGroup(void) |
| { |
| TRACE("()\n"); |
| return call_Scheduler_CreateScheduleGroup(get_current_scheduler()); |
| } |
| |
| /* ?GetNumberOfVirtualProcessors@CurrentScheduler@Concurrency@@SAIXZ */ |
| unsigned int __cdecl CurrentScheduler_GetNumberOfVirtualProcessors(void) |
| { |
| Scheduler *scheduler = try_get_current_scheduler(); |
| |
| TRACE("()\n"); |
| |
| if(!scheduler) |
| return -1; |
| return call_Scheduler_GetNumberOfVirtualProcessors(scheduler); |
| } |
| |
| /* ?GetPolicy@CurrentScheduler@Concurrency@@SA?AVSchedulerPolicy@2@XZ */ |
| SchedulerPolicy* __cdecl CurrentScheduler_GetPolicy(SchedulerPolicy *policy) |
| { |
| TRACE("(%p)\n", policy); |
| return call_Scheduler_GetPolicy(get_current_scheduler(), policy); |
| } |
| |
| /* ?Id@CurrentScheduler@Concurrency@@SAIXZ */ |
| unsigned int __cdecl CurrentScheduler_Id(void) |
| { |
| Scheduler *scheduler = try_get_current_scheduler(); |
| |
| TRACE("()\n"); |
| |
| if(!scheduler) |
| return -1; |
| return call_Scheduler_Id(scheduler); |
| } |
| |
| #if _MSVCR_VER > 100 |
| /* ?IsAvailableLocation@CurrentScheduler@Concurrency@@SA_NABVlocation@2@@Z */ |
| /* ?IsAvailableLocation@CurrentScheduler@Concurrency@@SA_NAEBVlocation@2@@Z */ |
| MSVCRT_bool __cdecl CurrentScheduler_IsAvailableLocation(const /*location*/void *placement) |
| { |
| Scheduler *scheduler = try_get_current_scheduler(); |
| |
| TRACE("(%p)\n", placement); |
| |
| if(!scheduler) |
| return FALSE; |
| return call_Scheduler_IsAvailableLocation(scheduler, placement); |
| } |
| #endif |
| |
| /* ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPAX@Z */ |
| /* ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPEAX@Z */ |
| void __cdecl CurrentScheduler_RegisterShutdownEvent(HANDLE event) |
| { |
| TRACE("(%p)\n", event); |
| call_Scheduler_RegisterShutdownEvent(get_current_scheduler(), event); |
| } |
| |
| #if _MSVCR_VER > 100 |
| /* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0AAVlocation@2@@Z */ |
| /* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPEAX@Z0AEAVlocation@2@@Z */ |
| void __cdecl CurrentScheduler_ScheduleTask_loc(void (__cdecl *proc)(void*), |
| void *data, /*location*/void *placement) |
| { |
| TRACE("(%p %p %p)\n", proc, data, placement); |
| call_Scheduler_ScheduleTask_loc(get_current_scheduler(), proc, data, placement); |
| } |
| #endif |
| |
| /* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0@Z */ |
| /* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPEAX@Z0@Z */ |
| void __cdecl CurrentScheduler_ScheduleTask(void (__cdecl *proc)(void*), void *data) |
| { |
| TRACE("(%p %p)\n", proc, data); |
| call_Scheduler_ScheduleTask(get_current_scheduler(), proc, data); |
| } |
| |
| /* ??0_Scheduler@details@Concurrency@@QAE@PAVScheduler@2@@Z */ |
| /* ??0_Scheduler@details@Concurrency@@QEAA@PEAVScheduler@2@@Z */ |
| DEFINE_THISCALL_WRAPPER(_Scheduler_ctor_sched, 8) |
| _Scheduler* __thiscall _Scheduler_ctor_sched(_Scheduler *this, Scheduler *scheduler) |
| { |
| TRACE("(%p %p)\n", this, scheduler); |
| |
| this->scheduler = scheduler; |
| return this; |
| } |
| |
| /* ??_F_Scheduler@details@Concurrency@@QAEXXZ */ |
| /* ??_F_Scheduler@details@Concurrency@@QEAAXXZ */ |
| DEFINE_THISCALL_WRAPPER(_Scheduler_ctor, 4) |
| _Scheduler* __thiscall _Scheduler_ctor(_Scheduler *this) |
| { |
| return _Scheduler_ctor_sched(this, NULL); |
| } |
| |
| /* ?_GetScheduler@_Scheduler@details@Concurrency@@QAEPAVScheduler@3@XZ */ |
| /* ?_GetScheduler@_Scheduler@details@Concurrency@@QEAAPEAVScheduler@3@XZ */ |
| DEFINE_THISCALL_WRAPPER(_Scheduler__GetScheduler, 4) |
| Scheduler* __thiscall _Scheduler__GetScheduler(_Scheduler *this) |
| { |
| TRACE("(%p)\n", this); |
| return this->scheduler; |
| } |
| |
| /* ?_Reference@_Scheduler@details@Concurrency@@QAEIXZ */ |
| /* ?_Reference@_Scheduler@details@Concurrency@@QEAAIXZ */ |
| DEFINE_THISCALL_WRAPPER(_Scheduler__Reference, 4) |
| unsigned int __thiscall _Scheduler__Reference(_Scheduler *this) |
| { |
| TRACE("(%p)\n", this); |
| return call_Scheduler_Reference(this->scheduler); |
| } |
| |
| /* ?_Release@_Scheduler@details@Concurrency@@QAEIXZ */ |
| /* ?_Release@_Scheduler@details@Concurrency@@QEAAIXZ */ |
| DEFINE_THISCALL_WRAPPER(_Scheduler__Release, 4) |
| unsigned int __thiscall _Scheduler__Release(_Scheduler *this) |
| { |
| TRACE("(%p)\n", this); |
| return call_Scheduler_Release(this->scheduler); |
| } |
| |
| /* ?_Get@_CurrentScheduler@details@Concurrency@@SA?AV_Scheduler@23@XZ */ |
| _Scheduler* __cdecl _CurrentScheduler__Get(_Scheduler *ret) |
| { |
| TRACE("()\n"); |
| return _Scheduler_ctor_sched(ret, get_current_scheduler()); |
| } |
| |
| /* ?_GetNumberOfVirtualProcessors@_CurrentScheduler@details@Concurrency@@SAIXZ */ |
| unsigned int __cdecl _CurrentScheduler__GetNumberOfVirtualProcessors(void) |
| { |
| TRACE("()\n"); |
| return CurrentScheduler_GetNumberOfVirtualProcessors(); |
| } |
| |
| /* ?_Id@_CurrentScheduler@details@Concurrency@@SAIXZ */ |
| unsigned int __cdecl _CurrentScheduler__Id(void) |
| { |
| TRACE("()\n"); |
| return CurrentScheduler_Id(); |
| } |
| |
| /* ?_ScheduleTask@_CurrentScheduler@details@Concurrency@@SAXP6AXPAX@Z0@Z */ |
| /* ?_ScheduleTask@_CurrentScheduler@details@Concurrency@@SAXP6AXPEAX@Z0@Z */ |
| void __cdecl _CurrentScheduler__ScheduleTask(void (__cdecl *proc)(void*), void *data) |
| { |
| TRACE("(%p %p)\n", proc, data); |
| CurrentScheduler_ScheduleTask(proc, data); |
| } |
| |
| extern const vtable_ptr MSVCRT_type_info_vtable; |
| DEFINE_RTTI_DATA0(Context, 0, ".?AVContext@Concurrency@@") |
| DEFINE_RTTI_DATA1(ContextBase, 0, &Context_rtti_base_descriptor, ".?AVContextBase@details@Concurrency@@") |
| DEFINE_RTTI_DATA2(ExternalContextBase, 0, &ContextBase_rtti_base_descriptor, |
| &Context_rtti_base_descriptor, ".?AVExternalContextBase@details@Concurrency@@") |
| DEFINE_RTTI_DATA0(Scheduler, 0, ".?AVScheduler@Concurrency@@") |
| DEFINE_RTTI_DATA1(SchedulerBase, 0, &Scheduler_rtti_base_descriptor, ".?AVSchedulerBase@details@Concurrency@@") |
| DEFINE_RTTI_DATA2(ThreadScheduler, 0, &SchedulerBase_rtti_base_descriptor, |
| &Scheduler_rtti_base_descriptor, ".?AVThreadScheduler@details@Concurrency@@") |
| |
| #ifndef __GNUC__ |
| void __asm_dummy_vtables(void) { |
| #endif |
| __ASM_VTABLE(ExternalContextBase, |
| VTABLE_ADD_FUNC(ExternalContextBase_GetId) |
| VTABLE_ADD_FUNC(ExternalContextBase_GetVirtualProcessorId) |
| VTABLE_ADD_FUNC(ExternalContextBase_GetScheduleGroupId) |
| VTABLE_ADD_FUNC(ExternalContextBase_Unblock) |
| VTABLE_ADD_FUNC(ExternalContextBase_IsSynchronouslyBlocked) |
| VTABLE_ADD_FUNC(ExternalContextBase_vector_dtor)); |
| __ASM_VTABLE(ThreadScheduler, |
| VTABLE_ADD_FUNC(ThreadScheduler_vector_dtor) |
| VTABLE_ADD_FUNC(ThreadScheduler_Id) |
| VTABLE_ADD_FUNC(ThreadScheduler_GetNumberOfVirtualProcessors) |
| VTABLE_ADD_FUNC(ThreadScheduler_GetPolicy) |
| VTABLE_ADD_FUNC(ThreadScheduler_Reference) |
| VTABLE_ADD_FUNC(ThreadScheduler_Release) |
| VTABLE_ADD_FUNC(ThreadScheduler_RegisterShutdownEvent) |
| VTABLE_ADD_FUNC(ThreadScheduler_Attach) |
| #if _MSVCR_VER > 100 |
| VTABLE_ADD_FUNC(ThreadScheduler_CreateScheduleGroup_loc) |
| #endif |
| VTABLE_ADD_FUNC(ThreadScheduler_CreateScheduleGroup) |
| #if _MSVCR_VER > 100 |
| VTABLE_ADD_FUNC(ThreadScheduler_ScheduleTask_loc) |
| #endif |
| VTABLE_ADD_FUNC(ThreadScheduler_ScheduleTask) |
| #if _MSVCR_VER > 100 |
| VTABLE_ADD_FUNC(ThreadScheduler_IsAvailableLocation) |
| #endif |
| ); |
| #ifndef __GNUC__ |
| } |
| #endif |
| |
| void msvcrt_init_scheduler(void *base) |
| { |
| #ifdef __x86_64__ |
| init_Context_rtti(base); |
| init_ContextBase_rtti(base); |
| init_ExternalContextBase_rtti(base); |
| init_Scheduler_rtti(base); |
| init_SchedulerBase_rtti(base); |
| init_ThreadScheduler_rtti(base); |
| #endif |
| } |
| |
| void msvcrt_free_scheduler(void) |
| { |
| if (context_tls_index != TLS_OUT_OF_INDEXES) |
| TlsFree(context_tls_index); |
| if(default_scheduler_policy.policy_container) |
| SchedulerPolicy_dtor(&default_scheduler_policy); |
| if(default_scheduler) { |
| ThreadScheduler_dtor(default_scheduler); |
| MSVCRT_operator_delete(default_scheduler); |
| } |
| } |
| |
| void msvcrt_free_scheduler_thread(void) |
| { |
| Context *context = try_get_current_context(); |
| if (!context) return; |
| call_Context_dtor(context, 1); |
| } |
| |
| #endif /* _MSVCR_VER >= 100 */ |