mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	Kernel: Added real support for thread and event blocking
- SVC: Added ExitThread support - SVC: Added SignalEvent support - Thread: Added WAITTYPE_EVENT for waiting threads for event signals - Thread: Added support for blocking on other threads to finish (e.g. Thread::Join) - Thread: Added debug function for printing current threads ready for execution - Thread: Removed hack/broken thread ready state code from Kernel::Reschedule - Mutex: Moved WaitCurrentThread from SVC to Mutex::WaitSynchronization - Event: Added support for blocking threads on event signalling Kernel: Added missing algorithm #include for use of std::find on non-Windows platforms.
This commit is contained in:
		@@ -3,12 +3,14 @@
 | 
			
		||||
// Refer to the license.txt file included.  
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/kernel/event.h"
 | 
			
		||||
#include "core/hle/kernel/thread.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
 | 
			
		||||
@@ -20,12 +22,13 @@ public:
 | 
			
		||||
    static Kernel::HandleType GetStaticHandleType() {  return Kernel::HandleType::Event; }
 | 
			
		||||
    Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
 | 
			
		||||
 | 
			
		||||
    ResetType intitial_reset_type;  ///< ResetType specified at Event initialization
 | 
			
		||||
    ResetType reset_type;           ///< Current ResetType
 | 
			
		||||
    ResetType intitial_reset_type;          ///< ResetType specified at Event initialization
 | 
			
		||||
    ResetType reset_type;                   ///< Current ResetType
 | 
			
		||||
 | 
			
		||||
    bool locked;                    ///< Current locked state
 | 
			
		||||
    bool permanent_locked;          ///< Hack - to set event permanent state (for easy passthrough)
 | 
			
		||||
    std::string name;               ///< Name of event (optional)
 | 
			
		||||
    bool locked;                            ///< Event signal wait
 | 
			
		||||
    bool permanent_locked;                  ///< Hack - to set event permanent state (for easy passthrough)
 | 
			
		||||
    std::vector<Handle> waiting_threads;    ///< Threads that are waiting for the event
 | 
			
		||||
    std::string name;                       ///< Name of event (optional)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize kernel object 
 | 
			
		||||
@@ -44,8 +47,14 @@ public:
 | 
			
		||||
     * @return Result of operation, 0 on success, otherwise error code
 | 
			
		||||
     */
 | 
			
		||||
    Result WaitSynchronization(bool* wait) {
 | 
			
		||||
        // TODO(bunnei): ImplementMe
 | 
			
		||||
        *wait = locked;
 | 
			
		||||
        if (locked) {
 | 
			
		||||
            Handle thread = GetCurrentThreadHandle();
 | 
			
		||||
            if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
 | 
			
		||||
                waiting_threads.push_back(thread);
 | 
			
		||||
            }
 | 
			
		||||
            Kernel::WaitCurrentThread(WAITTYPE_EVENT);
 | 
			
		||||
        }
 | 
			
		||||
        if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
 | 
			
		||||
            locked = true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -53,6 +62,22 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Hackish function to set an events permanent lock state, used to pass through synch blocks
 | 
			
		||||
 * @param handle Handle to event to change
 | 
			
		||||
 * @param permanent_locked Boolean permanent locked value to set event
 | 
			
		||||
 * @return Result of operation, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
Result SetPermanentLock(Handle handle, const bool permanent_locked) {
 | 
			
		||||
    Event* evt = g_object_pool.GetFast<Event>(handle);
 | 
			
		||||
    if (!evt) {
 | 
			
		||||
        ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    evt->permanent_locked = permanent_locked;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Changes whether an event is locked or not
 | 
			
		||||
 * @param handle Handle to event to change
 | 
			
		||||
@@ -72,18 +97,32 @@ Result SetEventLocked(const Handle handle, const bool locked) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Hackish function to set an events permanent lock state, used to pass through synch blocks
 | 
			
		||||
 * @param handle Handle to event to change
 | 
			
		||||
 * @param permanent_locked Boolean permanent locked value to set event
 | 
			
		||||
 * Signals an event
 | 
			
		||||
 * @param handle Handle to event to signal
 | 
			
		||||
 * @return Result of operation, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
Result SetPermanentLock(Handle handle, const bool permanent_locked) {
 | 
			
		||||
Result SignalEvent(const Handle handle) {
 | 
			
		||||
    Event* evt = g_object_pool.GetFast<Event>(handle);
 | 
			
		||||
    if (!evt) {
 | 
			
		||||
        ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    evt->permanent_locked = permanent_locked;
 | 
			
		||||
    // Resume threads waiting for event to signal
 | 
			
		||||
    bool event_caught = false;
 | 
			
		||||
    for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
 | 
			
		||||
        ResumeThreadFromWait( evt->waiting_threads[i]);
 | 
			
		||||
 | 
			
		||||
        // If any thread is signalled awake by this event, assume the event was "caught" and reset 
 | 
			
		||||
        // the event. This will result in the next thread waiting on the event to block. Otherwise,
 | 
			
		||||
        // the event will not be reset, and the next thread to call WaitSynchronization on it will
 | 
			
		||||
        // not block. Not sure if this is correct behavior, but it seems to work.
 | 
			
		||||
        event_caught = true;
 | 
			
		||||
    }
 | 
			
		||||
    evt->waiting_threads.clear();
 | 
			
		||||
 | 
			
		||||
    if (!evt->permanent_locked) {
 | 
			
		||||
        evt->locked = event_caught;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -93,7 +132,15 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
 | 
			
		||||
 * @return Result of operation, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
Result ClearEvent(Handle handle) {
 | 
			
		||||
    return SetEventLocked(handle, true);
 | 
			
		||||
    Event* evt = g_object_pool.GetFast<Event>(handle);
 | 
			
		||||
    if (!evt) {
 | 
			
		||||
        ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (!evt->permanent_locked) {
 | 
			
		||||
        evt->locked = true;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,13 @@ Result SetEventLocked(const Handle handle, const bool locked);
 | 
			
		||||
 */
 | 
			
		||||
Result SetPermanentLock(Handle handle, const bool permanent_locked);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Signals an event
 | 
			
		||||
 * @param handle Handle to event to signal
 | 
			
		||||
 * @return Result of operation, 0 on success, otherwise error code
 | 
			
		||||
 */
 | 
			
		||||
Result SignalEvent(const Handle handle);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Clears an event
 | 
			
		||||
 * @param handle Handle to event to clear
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,11 @@ public:
 | 
			
		||||
    Result WaitSynchronization(bool* wait) {
 | 
			
		||||
        // TODO(bunnei): ImplementMe
 | 
			
		||||
        *wait = locked;
 | 
			
		||||
 | 
			
		||||
        if (locked) {
 | 
			
		||||
            Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -52,7 +53,14 @@ public:
 | 
			
		||||
     * @return Result of operation, 0 on success, otherwise error code
 | 
			
		||||
     */
 | 
			
		||||
    Result WaitSynchronization(bool* wait) {
 | 
			
		||||
        // TODO(bunnei): ImplementMe
 | 
			
		||||
        if (status != THREADSTATUS_DORMANT) {
 | 
			
		||||
            Handle thread = GetCurrentThreadHandle();
 | 
			
		||||
            if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
 | 
			
		||||
                waiting_threads.push_back(thread);
 | 
			
		||||
            }
 | 
			
		||||
            WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
 | 
			
		||||
            *wait = true;
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +77,9 @@ public:
 | 
			
		||||
    s32 processor_id;
 | 
			
		||||
 | 
			
		||||
    WaitType wait_type;
 | 
			
		||||
    Handle wait_handle;
 | 
			
		||||
 | 
			
		||||
    std::vector<Handle> waiting_threads;
 | 
			
		||||
 | 
			
		||||
    char name[Kernel::MAX_NAME_LENGTH + 1];
 | 
			
		||||
};
 | 
			
		||||
@@ -82,7 +93,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
 | 
			
		||||
Handle g_current_thread_handle;
 | 
			
		||||
Thread* g_current_thread;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Gets the current thread
 | 
			
		||||
inline Thread* GetCurrentThread() {
 | 
			
		||||
    return g_current_thread;
 | 
			
		||||
@@ -114,15 +124,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
 | 
			
		||||
    memset(&t->context, 0, sizeof(ThreadContext));
 | 
			
		||||
 | 
			
		||||
    t->context.cpu_registers[0] = arg;
 | 
			
		||||
    t->context.pc = t->entry_point;
 | 
			
		||||
    t->context.pc = t->context.cpu_registers[15] = t->entry_point;
 | 
			
		||||
    t->context.sp = t->stack_top;
 | 
			
		||||
    t->context.cpsr = 0x1F; // Usermode
 | 
			
		||||
    
 | 
			
		||||
    if (t->current_priority < lowest_priority) {
 | 
			
		||||
        t->current_priority = t->initial_priority;
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
    t->wait_type = WAITTYPE_NONE;
 | 
			
		||||
    t->wait_handle = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Change a thread to "ready" state
 | 
			
		||||
@@ -142,6 +152,43 @@ void ChangeReadyState(Thread* t, bool ready) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Verify that a thread has not been released from waiting
 | 
			
		||||
inline bool VerifyWait(const Handle& thread, WaitType type, Handle handle) {
 | 
			
		||||
    Handle wait_id = 0;
 | 
			
		||||
    Thread *t = g_object_pool.GetFast<Thread>(thread);
 | 
			
		||||
    if (t) {
 | 
			
		||||
        if (type == t->wait_type && handle == t->wait_handle) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        ERROR_LOG(KERNEL, "thread 0x%08X does not exist", thread);
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Stops the current thread
 | 
			
		||||
void StopThread(Handle thread, const char* reason) {
 | 
			
		||||
    u32 error;
 | 
			
		||||
    Thread *t = g_object_pool.Get<Thread>(thread, error);
 | 
			
		||||
    if (t) {
 | 
			
		||||
        ChangeReadyState(t, false);
 | 
			
		||||
        t->status = THREADSTATUS_DORMANT;
 | 
			
		||||
        for (size_t i = 0; i < t->waiting_threads.size(); ++i) {
 | 
			
		||||
            const Handle waiting_thread = t->waiting_threads[i];
 | 
			
		||||
            if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, thread)) {
 | 
			
		||||
                ResumeThreadFromWait(waiting_thread);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        t->waiting_threads.clear();
 | 
			
		||||
 | 
			
		||||
        // Stopped threads are never waiting.
 | 
			
		||||
        t->wait_type = WAITTYPE_NONE;
 | 
			
		||||
        t->wait_handle = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        ERROR_LOG(KERNEL, "thread 0x%08X does not exist", thread);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Changes a threads state
 | 
			
		||||
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
 | 
			
		||||
    if (!t || t->status == new_status) {
 | 
			
		||||
@@ -152,7 +199,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
 | 
			
		||||
    
 | 
			
		||||
    if (new_status == THREADSTATUS_WAIT) {
 | 
			
		||||
        if (t->wait_type == WAITTYPE_NONE) {
 | 
			
		||||
            printf("ERROR: Waittype none not allowed here\n");
 | 
			
		||||
            ERROR_LOG(KERNEL, "Waittype none not allowed");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -207,9 +254,10 @@ Thread* NextThread() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Puts the current thread in the wait state for the given type
 | 
			
		||||
void WaitCurrentThread(WaitType wait_type) {
 | 
			
		||||
void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
 | 
			
		||||
    Thread* t = GetCurrentThread();
 | 
			
		||||
    t->wait_type = wait_type;
 | 
			
		||||
    t->wait_handle = wait_handle;
 | 
			
		||||
    ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -225,6 +273,22 @@ void ResumeThreadFromWait(Handle handle) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Prints the thread queue for debugging purposes
 | 
			
		||||
void DebugThreadQueue() {
 | 
			
		||||
    Thread* thread = GetCurrentThread();
 | 
			
		||||
    if (!thread) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
 | 
			
		||||
    for (u32 i = 0; i < g_thread_queue.size(); i++) {
 | 
			
		||||
        Handle handle = g_thread_queue[i];
 | 
			
		||||
        s32 priority = g_thread_ready_queue.contains(handle);
 | 
			
		||||
        if (priority != -1) {
 | 
			
		||||
            INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Creates a new thread
 | 
			
		||||
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
 | 
			
		||||
    s32 processor_id, u32 stack_top, int stack_size) {
 | 
			
		||||
@@ -233,12 +297,12 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
 | 
			
		||||
        "CreateThread priority=%d, outside of allowable range!", priority)
 | 
			
		||||
 | 
			
		||||
    Thread* t = new Thread;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    handle = Kernel::g_object_pool.Create(t);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    g_thread_queue.push_back(handle);
 | 
			
		||||
    g_thread_ready_queue.prepare(priority);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    t->status = THREADSTATUS_DORMANT;
 | 
			
		||||
    t->entry_point = entry_point;
 | 
			
		||||
    t->stack_top = stack_top;
 | 
			
		||||
@@ -246,16 +310,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
 | 
			
		||||
    t->initial_priority = t->current_priority = priority;
 | 
			
		||||
    t->processor_id = processor_id;
 | 
			
		||||
    t->wait_type = WAITTYPE_NONE;
 | 
			
		||||
    
 | 
			
		||||
    t->wait_handle = 0;
 | 
			
		||||
 | 
			
		||||
    strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
 | 
			
		||||
    t->name[Kernel::MAX_NAME_LENGTH] = '\0';
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Creates a new thread - wrapper for external user
 | 
			
		||||
Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
 | 
			
		||||
    u32 stack_top, int stack_size) {
 | 
			
		||||
 | 
			
		||||
    if (name == NULL) {
 | 
			
		||||
        ERROR_LOG(KERNEL, "CreateThread(): NULL name");
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -289,7 +355,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
 | 
			
		||||
 | 
			
		||||
    // This won't schedule to the new thread, but it may to one woken from eating cycles.
 | 
			
		||||
    // Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
 | 
			
		||||
    //HLE::Reschedule("thread created");
 | 
			
		||||
    //HLE::Reschedule(__func__);
 | 
			
		||||
    
 | 
			
		||||
    return handle;
 | 
			
		||||
}
 | 
			
		||||
@@ -363,35 +429,24 @@ Handle SetupMainThread(s32 priority, int stack_size) {
 | 
			
		||||
    return handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Reschedules to the next available thread (call after current thread is suspended)
 | 
			
		||||
void Reschedule() {
 | 
			
		||||
    Thread* prev = GetCurrentThread();
 | 
			
		||||
    Thread* next = NextThread();
 | 
			
		||||
    HLE::g_reschedule = false;
 | 
			
		||||
    if (next > 0) {
 | 
			
		||||
        INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        SwitchContext(next);
 | 
			
		||||
 | 
			
		||||
        // Hack - automatically change previous thread (which would have been in "wait" state) to
 | 
			
		||||
        // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to
 | 
			
		||||
        // actually wait for whatever event it is supposed to be waiting on.
 | 
			
		||||
 | 
			
		||||
        ChangeReadyState(prev, true);
 | 
			
		||||
    } else {
 | 
			
		||||
        INFO_LOG(KERNEL, "no ready threads, staying on 0x%08X", prev->GetHandle());
 | 
			
		||||
 | 
			
		||||
        // Hack - no other threads are available, so decrement current PC to the last instruction, 
 | 
			
		||||
        // and then resume current thread. This should always be called on a blocking instruction 
 | 
			
		||||
        // (e.g. svcWaitSynchronization), and the result should be that the instruction is repeated
 | 
			
		||||
        // until it no longer blocks. 
 | 
			
		||||
 | 
			
		||||
        // TODO(bunnei): A better solution: Have the CPU switch to an idle thread
 | 
			
		||||
 | 
			
		||||
        ThreadContext ctx;
 | 
			
		||||
        SaveContext(ctx);
 | 
			
		||||
        ctx.pc -= 4;
 | 
			
		||||
        LoadContext(ctx);
 | 
			
		||||
        ChangeReadyState(prev, true);
 | 
			
		||||
        // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
 | 
			
		||||
        // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
 | 
			
		||||
        // This results in the current thread yielding on a VBLANK once, and then it will be 
 | 
			
		||||
        // immediately placed back in the queue for execution.
 | 
			
		||||
        if (prev->wait_type == WAITTYPE_VBLANK) {
 | 
			
		||||
            ResumeThreadFromWait(prev->GetHandle());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ enum WaitType {
 | 
			
		||||
    WAITTYPE_NONE,
 | 
			
		||||
    WAITTYPE_SLEEP,
 | 
			
		||||
    WAITTYPE_SEMA,
 | 
			
		||||
    WAITTYPE_EVENTFLAG,
 | 
			
		||||
    WAITTYPE_EVENT,
 | 
			
		||||
    WAITTYPE_THREADEND,
 | 
			
		||||
    WAITTYPE_VBLANK,
 | 
			
		||||
    WAITTYPE_MUTEX,
 | 
			
		||||
@@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
 | 
			
		||||
/// Reschedules to the next available thread (call after current thread is suspended)
 | 
			
		||||
void Reschedule();
 | 
			
		||||
 | 
			
		||||
/// Puts the current thread in the wait state for the given type
 | 
			
		||||
void WaitCurrentThread(WaitType wait_type);
 | 
			
		||||
/// Stops the current thread
 | 
			
		||||
void StopThread(Handle thread, const char* reason);
 | 
			
		||||
 | 
			
		||||
/// Resumes a thread from waiting by marking it as "ready"
 | 
			
		||||
void ResumeThreadFromWait(Handle handle);
 | 
			
		||||
@@ -62,6 +62,9 @@ void ResumeThreadFromWait(Handle handle);
 | 
			
		||||
/// Gets the current thread handle
 | 
			
		||||
Handle GetCurrentThreadHandle();
 | 
			
		||||
 | 
			
		||||
/// Puts the current thread in the wait state for the given type
 | 
			
		||||
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
 | 
			
		||||
 | 
			
		||||
/// Put current thread in a wait state - on WaitSynchronization
 | 
			
		||||
void WaitThread_Synchronization();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user