/* * 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 #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(16); DEFINE_VTBL_WRAPPER(20); DEFINE_VTBL_WRAPPER(28); #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_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_Attach(this) CALL_VTBL_FUNC(this, 28, void, (Scheduler*), (this)) 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; 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; } /* ?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(); FIXME("()\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; iallocator_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; iallocator_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 < SchedulerKind || 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 < SchedulerKind || 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; ipolicy_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; ishutdown_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) { ExternalContextBase *context = (ExternalContextBase*)get_current_context(); TRACE("()\n"); if(context->context.vtable != &MSVCRT_ExternalContextBase_vtable) { ERR("unknown context set\n"); return NULL; } if(context->scheduler.scheduler) return context->scheduler.scheduler; create_default_scheduler(); context->scheduler.scheduler = &default_scheduler->scheduler; call_Scheduler_Reference(&default_scheduler->scheduler); return &default_scheduler->scheduler; } /* ?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) { FIXME("(%p) stub\n", placement); return NULL; } /* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@XZ */ /* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPEAVScheduleGroup@2@XZ */ /*ScheduleGroup*/void* __cdecl CurrentScheduler_CreateScheduleGroup(void) { FIXME("() stub\n"); return NULL; } /* ?GetNumberOfVirtualProcessors@CurrentScheduler@Concurrency@@SAIXZ */ unsigned int __cdecl CurrentScheduler_GetNumberOfVirtualProcessors(void) { FIXME("() stub\n"); return 0; } /* ?GetPolicy@CurrentScheduler@Concurrency@@SA?AVSchedulerPolicy@2@XZ */ SchedulerPolicy* __cdecl CurrentScheduler_GetPolicy(SchedulerPolicy *policy) { FIXME("(%p) stub\n", policy); return NULL; } /* ?Id@CurrentScheduler@Concurrency@@SAIXZ */ unsigned int __cdecl CurrentScheduler_Id(void) { FIXME("() stub\n"); return 0; } /* ?IsAvailableLocation@CurrentScheduler@Concurrency@@SA_NABVlocation@2@@Z */ /* ?IsAvailableLocation@CurrentScheduler@Concurrency@@SA_NAEBVlocation@2@@Z */ MSVCRT_bool __cdecl CurrentScheduler_IsAvailableLocation(const /*location*/void *placement) { FIXME("(%p) stub\n", placement); return 0; } /* ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPAX@Z */ /* ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPEAX@Z */ void __cdecl CurrentScheduler_RegisterShutdownEvent(HANDLE event) { FIXME("(%p) stub\n", event); } /* ?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) { FIXME("(%p %p %p) stub\n", proc, data, placement); } /* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0@Z */ /* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPEAX@Z0@Z */ void __cdecl CurrentScheduler_ScheduleTask(void (__cdecl *proc)(void*), void *data) { FIXME("(%p %p) stub\n", 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) VTABLE_ADD_FUNC(ThreadScheduler_CreateScheduleGroup_loc) VTABLE_ADD_FUNC(ThreadScheduler_CreateScheduleGroup) VTABLE_ADD_FUNC(ThreadScheduler_ScheduleTask_loc) VTABLE_ADD_FUNC(ThreadScheduler_ScheduleTask) VTABLE_ADD_FUNC(ThreadScheduler_IsAvailableLocation)); #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 */