zipped a lot of linux and windows code together related to multithreading and the system code linking
This commit is contained in:
parent
c97e82524e
commit
122cecb6ff
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 18.07.2017
|
||||
*
|
||||
* Code to link system functions using a name convention
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
// TODO(allen): Should auto-gen this!
|
||||
|
||||
#define SYSLINK(name) system->name = system_##name
|
||||
|
||||
internal void
|
||||
link_system_code(System_Functions *system){
|
||||
SYSLINK(set_file_list);
|
||||
SYSLINK(get_canonical);
|
||||
SYSLINK(add_listener);
|
||||
SYSLINK(remove_listener);
|
||||
SYSLINK(get_file_change);
|
||||
SYSLINK(load_handle);
|
||||
SYSLINK(load_size);
|
||||
SYSLINK(load_file);
|
||||
SYSLINK(load_close);
|
||||
SYSLINK(save_file);
|
||||
|
||||
SYSLINK(now_time);
|
||||
|
||||
SYSLINK(post_clipboard);
|
||||
|
||||
SYSLINK(create_coroutine);
|
||||
SYSLINK(launch_coroutine);
|
||||
SYSLINK(resume_coroutine);
|
||||
SYSLINK(yield_coroutine);
|
||||
|
||||
SYSLINK(cli_call);
|
||||
SYSLINK(cli_begin_update);
|
||||
SYSLINK(cli_update_step);
|
||||
SYSLINK(cli_end_update);
|
||||
|
||||
SYSLINK(post_job);
|
||||
SYSLINK(cancel_job);
|
||||
SYSLINK(check_cancel);
|
||||
SYSLINK(grow_thread_memory);
|
||||
SYSLINK(acquire_lock);
|
||||
SYSLINK(release_lock);
|
||||
|
||||
SYSLINK(memory_allocate);
|
||||
SYSLINK(memory_set_protection);
|
||||
SYSLINK(memory_free);
|
||||
SYSLINK(file_exists);
|
||||
SYSLINK(directory_cd);
|
||||
SYSLINK(get_4ed_path);
|
||||
SYSLINK(toggle_fullscreen);
|
||||
SYSLINK(is_fullscreen);
|
||||
SYSLINK(show_mouse_cursor);
|
||||
SYSLINK(send_exit_signal);
|
||||
|
||||
SYSLINK(log);
|
||||
#if FRED_INTERNAL
|
||||
SYSLINK(internal_get_thread_states);
|
||||
#endif
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
|
@ -92,7 +92,7 @@ get_work_queue_available_space(i32 write, i32 read){
|
|||
#define UNBOUNDED_SKIP_MAX 128
|
||||
|
||||
internal i32
|
||||
flush_unbounded_queue_to_main(Unbounded_Work_Queue *source_queue, Work_Queue *queue, i32 thread_count){
|
||||
flush_to_direct_queue(Unbounded_Work_Queue *source_queue, Work_Queue *queue, i32 thread_count){
|
||||
// NOTE(allen): It is understood that read_position may be changed by other
|
||||
// threads but it will only make more space in the queue if it is changed.
|
||||
// Meanwhile write_position should not ever be changed by anything but the
|
||||
|
@ -144,8 +144,127 @@ flush_unbounded_queue_to_main(Unbounded_Work_Queue *source_queue, Work_Queue *qu
|
|||
semaphore_release_count = thread_count;
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < semaphore_release_count; ++i){
|
||||
system_release_semaphore(queue->semaphore);
|
||||
}
|
||||
|
||||
return(semaphore_release_count);
|
||||
}
|
||||
|
||||
// Note(allen): post_job puts the job on the unbounded queue.
|
||||
// The unbounded queue is entirely managed by the main thread.
|
||||
// The thread safe queue is bounded in size so the unbounded
|
||||
// queue is periodically flushed into the direct work queue.
|
||||
internal u32
|
||||
post_job(Thread_Group *group, Work_Queue *direct_queue, Job_Data job){
|
||||
Unbounded_Work_Queue *queue = &group->queue;
|
||||
|
||||
u32 result = queue->next_job_id++;
|
||||
|
||||
if (queue->count >= queue->max){
|
||||
u32 new_max = round_up_pot_u32(queue->count + 1);
|
||||
Full_Job_Data *new_jobs = (Full_Job_Data*)system_memory_allocate(new_max*sizeof(Full_Job_Data));
|
||||
memcpy(new_jobs, queue->jobs, queue->count);
|
||||
system_memory_free(queue->jobs, queue->max*sizeof(Full_Job_Data));
|
||||
queue->jobs = new_jobs;
|
||||
queue->max = new_max;
|
||||
}
|
||||
|
||||
Full_Job_Data full_job = {0};
|
||||
full_job.job = job;
|
||||
full_job.running_thread = THREAD_NOT_ASSIGNED;
|
||||
full_job.id = result;
|
||||
|
||||
queue->jobs[queue->count++] = full_job;
|
||||
flush_to_direct_queue(queue, direct_queue, group->count);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
cancel_job(Thread_Group *group, Work_Queue *queue, u32 job_id){
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
|
||||
b32 handled_in_unbounded = false;
|
||||
if (source_queue->skip < source_queue->count){
|
||||
Full_Job_Data *first_job = source_queue->jobs + source_queue->skip;
|
||||
if (first_job->id <= job_id){
|
||||
u32 index = source_queue->skip + (job_id - first_job->id);
|
||||
Full_Job_Data *job = source_queue->jobs + index;
|
||||
job->running_thread = 0;
|
||||
handled_in_unbounded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled_in_unbounded){
|
||||
Full_Job_Data *job = queue->jobs + (job_id % QUEUE_WRAP);
|
||||
Assert(job->id == job_id);
|
||||
|
||||
u32 thread_id = InterlockedCompareExchange(&job->running_thread, 0, THREAD_NOT_ASSIGNED);
|
||||
|
||||
if (thread_id != THREAD_NOT_ASSIGNED && thread_id != 0){
|
||||
i32 thread_index = thread_id - 1;
|
||||
|
||||
i32 cancel_lock = group->cancel_lock0 + thread_index;
|
||||
i32 cancel_cv = group->cancel_cv0 + thread_index;
|
||||
Thread_Context *thread = group->threads + thread_index;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
thread->cancel = true;
|
||||
system_release_lock(FRAME_LOCK);
|
||||
|
||||
do{
|
||||
system_wait_cv(cancel_lock, cancel_cv);
|
||||
}while (thread->cancel);
|
||||
|
||||
system_acquire_lock(FRAME_LOCK);
|
||||
system_release_lock(cancel_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal b32
|
||||
check_cancel_status(Thread_Group *group, Thread_Context *thread){
|
||||
b32 result = false;
|
||||
i32 thread_index = thread->id - 1;
|
||||
i32 cancel_lock = group->cancel_lock0 + thread_index;
|
||||
system_acquire_lock(cancel_lock);
|
||||
if (thread->cancel){
|
||||
result = true;
|
||||
}
|
||||
system_release_lock(cancel_lock);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
grow_thread_memory(Thread_Memory *memory){
|
||||
system_acquire_lock(CANCEL_LOCK0 + memory->id - 1);
|
||||
void *old_data = memory->data;
|
||||
i32 old_size = memory->size;
|
||||
i32 new_size = l_round_up_i32(memory->size*2, KB(4));
|
||||
memory->data = system_memory_allocate(new_size);
|
||||
memory->size = new_size;
|
||||
if (old_data != 0){
|
||||
memcpy(memory->data, old_data, old_size);
|
||||
system_memory_free(old_data, old_size);
|
||||
}
|
||||
system_release_lock(CANCEL_LOCK0 + memory->id - 1);
|
||||
}
|
||||
|
||||
internal void
|
||||
dbg_get_thread_states(Thread_Group *group, Work_Queue *queue, b8 *running, i32 *pending){
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
u32 write = queue->write_position;
|
||||
u32 read = queue->read_position;
|
||||
if (write < read){
|
||||
write += QUEUE_WRAP;
|
||||
}
|
||||
*pending = (i32)(write - read) + source_queue->count - source_queue->skip;
|
||||
|
||||
for (i32 i = 0; i < group->count; ++i){
|
||||
running[i] = (group->threads[i].running != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
||||
|
|
|
@ -133,6 +133,14 @@ struct Thread_Group{
|
|||
i32 cancel_cv0;
|
||||
};
|
||||
|
||||
struct Mutex{
|
||||
pthread_mutex_t crit;
|
||||
};
|
||||
|
||||
struct Condition_Variable{
|
||||
pthread_cond_t cv;
|
||||
};
|
||||
|
||||
struct Linux_Vars{
|
||||
Display *XDisplay;
|
||||
Window XWindow;
|
||||
|
@ -185,8 +193,8 @@ struct Linux_Vars{
|
|||
Thread_Memory *thread_memory;
|
||||
Thread_Group groups[THREAD_GROUP_COUNT];
|
||||
Work_Queue queues[THREAD_GROUP_COUNT];
|
||||
pthread_mutex_t locks[LOCK_COUNT];
|
||||
pthread_cond_t conds[8];
|
||||
Mutex locks[LOCK_COUNT];
|
||||
Condition_Variable conds[8];
|
||||
sem_t thread_semaphore;
|
||||
|
||||
i32 dpi_x, dpi_y;
|
||||
|
@ -483,14 +491,24 @@ Sys_CLI_End_Update_Sig(system_cli_end_update){
|
|||
// Threads
|
||||
//
|
||||
|
||||
internal void
|
||||
system_internal_acquire_lock(Mutex *m){
|
||||
pthread_mutex_lock(m->crit);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_internal_release_lock(Mutex *m){
|
||||
pthread_mutex_unlock(m->crit);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Acquire_Lock_Sig(system_acquire_lock){
|
||||
pthread_mutex_lock(linuxvars.locks + id);
|
||||
system_internal_acquire_lock(&linuxvars.locks[id]);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Release_Lock_Sig(system_release_lock){
|
||||
pthread_mutex_unlock(linuxvars.locks + id);
|
||||
system_internal_release_lock(&linuxvars.locks[id]);
|
||||
}
|
||||
|
||||
internal void
|
||||
|
@ -514,6 +532,11 @@ system_wait_on(Plat_Handle handle){
|
|||
sem_wait(LinuxHandleToSem(handle));
|
||||
}
|
||||
|
||||
internal void
|
||||
system_release_semaphore(Plat_Handle handle){
|
||||
sem_post(LinuxHandleToSem(handle));
|
||||
}
|
||||
|
||||
#include "4ed_work_queues.cpp"
|
||||
|
||||
internal void*
|
||||
|
@ -528,22 +551,6 @@ JobThreadProc(void* lpParameter){
|
|||
return(0);
|
||||
}
|
||||
|
||||
internal void
|
||||
flush_to_direct_queue(Unbounded_Work_Queue *source_queue, Work_Queue *queue, i32 thread_count){
|
||||
i32 semaphore_release_count = flush_to_direct_queue(source_queue, queue, thread_count);
|
||||
for (i32 i = 0; i < semaphore_release_count; ++i){
|
||||
sem_post(LinuxHandleToSem(queue->semaphore));
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
flush_thread_group(i32 group_id){
|
||||
Thread_Group *group = linuxvars.groups + group_id;
|
||||
Work_Queue *queue = linuxvars.queues + group_id;
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
flush_to_direct_queue(source_queue, queue, group->count);
|
||||
}
|
||||
|
||||
// Note(allen): post_job puts the job on the unbounded queue.
|
||||
// The unbounded queue is entirely managed by the main thread.
|
||||
// The thread safe queue is bounded in size so the unbounded
|
||||
|
@ -551,152 +558,36 @@ flush_thread_group(i32 group_id){
|
|||
internal
|
||||
Sys_Post_Job_Sig(system_post_job){
|
||||
Thread_Group *group = linuxvars.groups + group_id;
|
||||
Unbounded_Work_Queue *queue = &group->queue;
|
||||
|
||||
u32 result = queue->next_job_id++;
|
||||
|
||||
while (queue->count >= queue->max){
|
||||
i32 new_max = queue->max*2;
|
||||
u32 job_size = sizeof(Full_Job_Data);
|
||||
Full_Job_Data *new_jobs = (Full_Job_Data*)system_memory_allocate(new_max*job_size);
|
||||
|
||||
memcpy(new_jobs, queue->jobs, queue->count);
|
||||
|
||||
system_memory_free(queue->jobs, queue->max*job_size);
|
||||
|
||||
queue->jobs = new_jobs;
|
||||
queue->max = new_max;
|
||||
}
|
||||
|
||||
Full_Job_Data full_job;
|
||||
|
||||
full_job.job = job;
|
||||
full_job.running_thread = THREAD_NOT_ASSIGNED;
|
||||
full_job.id = result;
|
||||
|
||||
queue->jobs[queue->count++] = full_job;
|
||||
|
||||
Work_Queue *direct_queue = linuxvars.queues + group_id;
|
||||
flush_to_direct_queue(queue, direct_queue, group->count);
|
||||
|
||||
u32 result = post_job(group, direct_queue, job);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Cancel_Job_Sig(system_cancel_job){
|
||||
Thread_Group *group = linuxvars.groups + group_id;
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
|
||||
b32 handled_in_unbounded = false;
|
||||
if (source_queue->skip < source_queue->count){
|
||||
Full_Job_Data *first_job = source_queue->jobs + source_queue->skip;
|
||||
if (first_job->id <= job_id){
|
||||
u32 index = source_queue->skip + (job_id - first_job->id);
|
||||
Full_Job_Data *job = source_queue->jobs + index;
|
||||
job->running_thread = 0;
|
||||
handled_in_unbounded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled_in_unbounded){
|
||||
Work_Queue *queue = linuxvars.queues + group_id;
|
||||
Full_Job_Data *job = queue->jobs + (job_id % QUEUE_WRAP);
|
||||
Assert(job->id == job_id);
|
||||
|
||||
u32 thread_id = InterlockedCompareExchange(&job->running_thread, 0, THREAD_NOT_ASSIGNED);
|
||||
|
||||
if (thread_id != THREAD_NOT_ASSIGNED && thread_id != 0){
|
||||
i32 thread_index = thread_id - 1;
|
||||
|
||||
i32 cancel_lock = group->cancel_lock0 + thread_index;
|
||||
i32 cancel_cv = group->cancel_cv0 + thread_index;
|
||||
Thread_Context *thread = group->threads + thread_index;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
|
||||
thread->cancel = 1;
|
||||
|
||||
system_release_lock(FRAME_LOCK);
|
||||
do{
|
||||
system_wait_cv(cancel_lock, cancel_cv);
|
||||
}while (thread->cancel == 1);
|
||||
system_acquire_lock(FRAME_LOCK);
|
||||
|
||||
system_release_lock(cancel_lock);
|
||||
}
|
||||
}
|
||||
cancel_job(group, queue, job_id);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Check_Cancel_Sig(system_check_cancel){
|
||||
b32 result = false;
|
||||
|
||||
Thread_Group *group = linuxvars.groups + thread->group_id;
|
||||
i32 thread_index = thread->id - 1;
|
||||
i32 cancel_lock = group->cancel_lock0 + thread_index;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
if (thread->cancel){
|
||||
result = true;
|
||||
}
|
||||
system_release_lock(cancel_lock);
|
||||
|
||||
b32 result = check_cancel_status(group, thread);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
|
||||
system_acquire_lock(CANCEL_LOCK0 + memory->id - 1);
|
||||
void *old_data = memory->data;
|
||||
i32 old_size = memory->size;
|
||||
i32 new_size = l_round_up_i32(memory->size*2, KB(4));
|
||||
memory->data = system_memory_allocate(new_size);
|
||||
memory->size = new_size;
|
||||
if (old_data){
|
||||
memcpy(memory->data, old_data, old_size);
|
||||
system_memory_free(old_data, old_size);
|
||||
}
|
||||
system_release_lock(CANCEL_LOCK0 + memory->id - 1);
|
||||
grow_thread_memory(memory);
|
||||
}
|
||||
|
||||
//
|
||||
// Debug
|
||||
//
|
||||
|
||||
#if FRED_INTERNAL
|
||||
|
||||
#if defined(OLD_JOB_QUEUE)
|
||||
internal
|
||||
INTERNAL_Sys_Get_Thread_States_Sig(internal_get_thread_states){
|
||||
Work_Queue *queue = linuxvars.queues + id;
|
||||
u32 write = queue->write_position;
|
||||
u32 read = queue->read_position;
|
||||
if (write < read) write += QUEUE_WRAP;
|
||||
*pending = (i32)(write - read);
|
||||
|
||||
INTERNAL_Sys_Get_Thread_States_Sig(system_internal_get_thread_states){
|
||||
Thread_Group *group = linuxvars.groups + id;
|
||||
for (i32 i = 0; i < group->count; ++i){
|
||||
running[i] = (group->threads[i].running != 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
internal
|
||||
INTERNAL_Sys_Get_Thread_States_Sig(internal_get_thread_states){
|
||||
Thread_Group *group = linuxvars.groups + id;
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
Work_Queue *queue = linuxvars.queues + id;
|
||||
u32 write = queue->write_position;
|
||||
u32 read = queue->read_position;
|
||||
if (write < read) write += QUEUE_WRAP;
|
||||
*pending = (i32)(write - read) + source_queue->count - source_queue->skip;
|
||||
|
||||
for (i32 i = 0; i < group->count; ++i){
|
||||
running[i] = (group->threads[i].running != 0);
|
||||
dbg_get_thread_states(group, queue, running, pending);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
// Linux rendering/font system functions
|
||||
|
@ -738,63 +629,11 @@ LinuxLoadAppCode(String* base_dir){
|
|||
return(result);
|
||||
}
|
||||
|
||||
#include "4ed_link_system_functions.cpp"
|
||||
|
||||
internal void
|
||||
LinuxLoadSystemCode(){
|
||||
// files
|
||||
linuxvars.system.set_file_list = system_set_file_list;
|
||||
linuxvars.system.get_canonical = system_get_canonical;
|
||||
linuxvars.system.add_listener = system_add_listener;
|
||||
linuxvars.system.remove_listener = system_remove_listener;
|
||||
linuxvars.system.get_file_change = system_get_file_change;
|
||||
linuxvars.system.load_handle = system_load_handle;
|
||||
linuxvars.system.load_size = system_load_size;
|
||||
linuxvars.system.load_file = system_load_file;
|
||||
linuxvars.system.load_close = system_load_close;
|
||||
linuxvars.system.save_file = system_save_file;
|
||||
|
||||
// time
|
||||
linuxvars.system.now_time = system_now_time;
|
||||
|
||||
// custom.h
|
||||
linuxvars.system.memory_allocate = system_memory_allocate;
|
||||
linuxvars.system.memory_set_protection = system_memory_set_protection;
|
||||
linuxvars.system.memory_free = system_memory_free;
|
||||
linuxvars.system.file_exists = system_file_exists;
|
||||
linuxvars.system.directory_cd = system_directory_cd;
|
||||
linuxvars.system.get_4ed_path = system_get_4ed_path;
|
||||
linuxvars.system.show_mouse_cursor = system_show_mouse_cursor;
|
||||
linuxvars.system.toggle_fullscreen = system_toggle_fullscreen;
|
||||
linuxvars.system.is_fullscreen = system_is_fullscreen;
|
||||
linuxvars.system.send_exit_signal = system_send_exit_signal;
|
||||
|
||||
// clipboard
|
||||
linuxvars.system.post_clipboard = system_post_clipboard;
|
||||
|
||||
// coroutine
|
||||
linuxvars.system.create_coroutine = system_create_coroutine;
|
||||
linuxvars.system.launch_coroutine = system_launch_coroutine;
|
||||
linuxvars.system.resume_coroutine = system_resume_coroutine;
|
||||
linuxvars.system.yield_coroutine = system_yield_coroutine;
|
||||
|
||||
// cli
|
||||
linuxvars.system.cli_call = system_cli_call;
|
||||
linuxvars.system.cli_begin_update = system_cli_begin_update;
|
||||
linuxvars.system.cli_update_step = system_cli_update_step;
|
||||
linuxvars.system.cli_end_update = system_cli_end_update;
|
||||
|
||||
// threads
|
||||
linuxvars.system.post_job = system_post_job;
|
||||
linuxvars.system.cancel_job = system_cancel_job;
|
||||
linuxvars.system.check_cancel = system_check_cancel;
|
||||
linuxvars.system.grow_thread_memory = system_grow_thread_memory;
|
||||
linuxvars.system.acquire_lock = system_acquire_lock;
|
||||
linuxvars.system.release_lock = system_release_lock;
|
||||
|
||||
// debug
|
||||
linuxvars.system.log = system_log;
|
||||
#if FRED_INTERNAL
|
||||
linuxvars.system.internal_get_thread_states = internal_get_thread_states;
|
||||
#endif
|
||||
link_system_code(&linuxvars.system);
|
||||
}
|
||||
|
||||
internal void
|
||||
|
|
|
@ -86,6 +86,55 @@
|
|||
|
||||
#define WM_4coder_ANIMATE (WM_USER + 0)
|
||||
|
||||
struct Control_Keys{
|
||||
b8 l_ctrl;
|
||||
b8 r_ctrl;
|
||||
b8 l_alt;
|
||||
b8 r_alt;
|
||||
};
|
||||
global Control_Keys null_control_keys = {0};
|
||||
|
||||
struct Win32_Input_Chunk_Transient{
|
||||
Key_Input_Data key_data;
|
||||
b8 mouse_l_press, mouse_l_release;
|
||||
b8 mouse_r_press, mouse_r_release;
|
||||
b8 out_of_window;
|
||||
i8 mouse_wheel;
|
||||
b8 trying_to_kill;
|
||||
};
|
||||
global Win32_Input_Chunk_Transient null_input_chunk_transient = {0};
|
||||
|
||||
struct Win32_Input_Chunk_Persistent{
|
||||
i32 mouse_x, mouse_y;
|
||||
b8 mouse_l, mouse_r;
|
||||
|
||||
Control_Keys controls;
|
||||
b8 control_keys[MDFR_INDEX_COUNT];
|
||||
};
|
||||
|
||||
struct Win32_Input_Chunk{
|
||||
Win32_Input_Chunk_Transient trans;
|
||||
Win32_Input_Chunk_Persistent pers;
|
||||
};
|
||||
|
||||
struct Win32_Coroutine{
|
||||
Coroutine coroutine;
|
||||
Win32_Coroutine *next;
|
||||
i32 done;
|
||||
};
|
||||
|
||||
enum CV_ID{
|
||||
CANCEL_CV0,
|
||||
CANCEL_CV1,
|
||||
CANCEL_CV2,
|
||||
CANCEL_CV3,
|
||||
CANCEL_CV4,
|
||||
CANCEL_CV5,
|
||||
CANCEL_CV6,
|
||||
CANCEL_CV7,
|
||||
CV_COUNT
|
||||
};
|
||||
|
||||
struct Thread_Context{
|
||||
u32 job_id;
|
||||
b32 running;
|
||||
|
@ -108,53 +157,12 @@ struct Thread_Group{
|
|||
i32 cancel_cv0;
|
||||
};
|
||||
|
||||
struct Control_Keys{
|
||||
b8 l_ctrl;
|
||||
b8 r_ctrl;
|
||||
b8 l_alt;
|
||||
b8 r_alt;
|
||||
};
|
||||
static Control_Keys null_control_keys = {0};
|
||||
|
||||
struct Win32_Input_Chunk_Transient{
|
||||
Key_Input_Data key_data;
|
||||
b8 mouse_l_press, mouse_l_release;
|
||||
b8 mouse_r_press, mouse_r_release;
|
||||
b8 out_of_window;
|
||||
i8 mouse_wheel;
|
||||
b8 trying_to_kill;
|
||||
};
|
||||
static Win32_Input_Chunk_Transient null_input_chunk_transient = {0};
|
||||
|
||||
struct Win32_Input_Chunk_Persistent{
|
||||
i32 mouse_x, mouse_y;
|
||||
b8 mouse_l, mouse_r;
|
||||
|
||||
Control_Keys controls;
|
||||
b8 control_keys[MDFR_INDEX_COUNT];
|
||||
struct Mutex{
|
||||
CRITICAL_SECTION crit;
|
||||
};
|
||||
|
||||
typedef struct Win32_Input_Chunk{
|
||||
Win32_Input_Chunk_Transient trans;
|
||||
Win32_Input_Chunk_Persistent pers;
|
||||
} Win32_Input_Chunk;
|
||||
|
||||
typedef struct Win32_Coroutine{
|
||||
Coroutine coroutine;
|
||||
Win32_Coroutine *next;
|
||||
i32 done;
|
||||
} Win32_Coroutine;
|
||||
|
||||
enum CV_ID{
|
||||
CANCEL_CV0,
|
||||
CANCEL_CV1,
|
||||
CANCEL_CV2,
|
||||
CANCEL_CV3,
|
||||
CANCEL_CV4,
|
||||
CANCEL_CV5,
|
||||
CANCEL_CV6,
|
||||
CANCEL_CV7,
|
||||
CV_COUNT
|
||||
struct Condition_Variable{
|
||||
CONDITION_VARIABLE cv;
|
||||
};
|
||||
|
||||
struct Win32_Vars{
|
||||
|
@ -165,16 +173,15 @@ struct Win32_Vars{
|
|||
HMODULE custom;
|
||||
Plat_Settings settings;
|
||||
|
||||
|
||||
Thread_Memory *thread_memory;
|
||||
Work_Queue queues[THREAD_GROUP_COUNT];
|
||||
Thread_Group groups[THREAD_GROUP_COUNT];
|
||||
CRITICAL_SECTION locks[LOCK_COUNT];
|
||||
CONDITION_VARIABLE condition_vars[CV_COUNT];
|
||||
Thread_Memory *thread_memory;
|
||||
Mutex locks[LOCK_COUNT];
|
||||
Condition_Variable condition_vars[CV_COUNT];
|
||||
|
||||
Win32_Coroutine coroutine_data[18];
|
||||
Win32_Coroutine *coroutine_free;
|
||||
|
||||
|
||||
Win32_Input_Chunk input_chunk;
|
||||
b32 lctrl_lalt_is_altgr;
|
||||
b32 got_useful_event;
|
||||
|
@ -336,25 +343,34 @@ Sys_Memory_Free_Sig(system_memory_free){
|
|||
// Multithreading
|
||||
//
|
||||
|
||||
internal void
|
||||
system_internal_acquire_lock(Mutex *m){
|
||||
EnterCriticalSection(&m->crit);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_internal_release_lock(Mutex *m){
|
||||
LeaveCriticalSection(&m->crit);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Acquire_Lock_Sig(system_acquire_lock){
|
||||
EnterCriticalSection(&win32vars.locks[id]);
|
||||
system_internal_acquire_lock(&win32vars.locks[id]);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Release_Lock_Sig(system_release_lock){
|
||||
LeaveCriticalSection(&win32vars.locks[id]);
|
||||
system_internal_release_lock(&win32vars.locks[id]);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_wait_cv(i32 crit_id, i32 cv_id){
|
||||
SleepConditionVariableCS(win32vars.condition_vars + cv_id, win32vars.locks + crit_id, INFINITE);
|
||||
SleepConditionVariableCS(&win32vars.condition_vars[cv_id].cv, &win32vars.locks[crit_id].crit, INFINITE);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_signal_cv(i32 crit_id, i32 cv_id){
|
||||
AllowLocal(crit_id);
|
||||
WakeConditionVariable(win32vars.condition_vars + cv_id);
|
||||
WakeConditionVariable(&win32vars.condition_vars[cv_id].cv);
|
||||
}
|
||||
|
||||
internal void
|
||||
|
@ -367,6 +383,11 @@ system_wait_on(Plat_Handle handle){
|
|||
WaitForSingleObject(Win32Handle(handle), INFINITE);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_release_semaphore(Plat_Handle handle){
|
||||
ReleaseSemaphore(Win32Handle(handle), 1, 0);
|
||||
}
|
||||
|
||||
#include "4ed_work_queues.cpp"
|
||||
|
||||
internal DWORD CALL_CONVENTION
|
||||
|
@ -381,14 +402,6 @@ JobThreadProc(LPVOID lpParameter){
|
|||
return(0);
|
||||
}
|
||||
|
||||
internal void
|
||||
flush_to_direct_queue(Unbounded_Work_Queue *source_queue, Work_Queue *queue, i32 thread_count){
|
||||
i32 semaphore_release_count = flush_unbounded_queue_to_main(source_queue, queue, thread_count);
|
||||
for (i32 i = 0; i < semaphore_release_count; ++i){
|
||||
ReleaseSemaphore(Win32Handle(queue->semaphore), 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
flush_thread_group(i32 group_id){
|
||||
Thread_Group *group = win32vars.groups + group_id;
|
||||
|
@ -397,141 +410,39 @@ flush_thread_group(i32 group_id){
|
|||
flush_to_direct_queue(source_queue, queue, group->count);
|
||||
}
|
||||
|
||||
// Note(allen): post_job puts the job on the unbounded queue.
|
||||
// The unbounded queue is entirely managed by the main thread.
|
||||
// The thread safe queue is bounded in size so the unbounded
|
||||
// queue is periodically flushed into the direct work queue.
|
||||
internal
|
||||
Sys_Post_Job_Sig(system_post_job){
|
||||
Thread_Group *group = win32vars.groups + group_id;
|
||||
Unbounded_Work_Queue *queue = &group->queue;
|
||||
|
||||
u32 result = queue->next_job_id++;
|
||||
|
||||
while (queue->count >= queue->max){
|
||||
u32 new_max = queue->max*2;
|
||||
Full_Job_Data *new_jobs = (Full_Job_Data*)system_memory_allocate(new_max*sizeof(Full_Job_Data));
|
||||
|
||||
memcpy(new_jobs, queue->jobs, queue->count);
|
||||
|
||||
system_memory_free(queue->jobs, 0);
|
||||
|
||||
queue->jobs = new_jobs;
|
||||
queue->max = new_max;
|
||||
}
|
||||
|
||||
Full_Job_Data full_job;
|
||||
|
||||
full_job.job = job;
|
||||
full_job.running_thread = THREAD_NOT_ASSIGNED;
|
||||
full_job.id = result;
|
||||
|
||||
queue->jobs[queue->count++] = full_job;
|
||||
|
||||
Work_Queue *direct_queue = win32vars.queues + group_id;
|
||||
flush_to_direct_queue(queue, direct_queue, group->count);
|
||||
|
||||
u32 result = post_job(group, direct_queue, job);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Cancel_Job_Sig(system_cancel_job){
|
||||
Thread_Group *group = win32vars.groups + group_id;
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
|
||||
b32 handled_in_unbounded = false;
|
||||
if (source_queue->skip < source_queue->count){
|
||||
Full_Job_Data *first_job = source_queue->jobs + source_queue->skip;
|
||||
if (first_job->id <= job_id){
|
||||
u32 index = source_queue->skip + (job_id - first_job->id);
|
||||
Full_Job_Data *job = source_queue->jobs + index;
|
||||
job->running_thread = 0;
|
||||
handled_in_unbounded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled_in_unbounded){
|
||||
Work_Queue *queue = win32vars.queues + group_id;
|
||||
Full_Job_Data *job = queue->jobs + (job_id % QUEUE_WRAP);
|
||||
Assert(job->id == job_id);
|
||||
|
||||
u32 thread_id =
|
||||
InterlockedCompareExchange(&job->running_thread,
|
||||
0, THREAD_NOT_ASSIGNED);
|
||||
|
||||
if (thread_id != THREAD_NOT_ASSIGNED && thread_id != 0){
|
||||
i32 thread_index = thread_id - 1;
|
||||
|
||||
i32 cancel_lock = group->cancel_lock0 + thread_index;
|
||||
i32 cancel_cv = group->cancel_cv0 + thread_index;
|
||||
Thread_Context *thread = group->threads + thread_index;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
|
||||
thread->cancel = 1;
|
||||
|
||||
system_release_lock(FRAME_LOCK);
|
||||
do{
|
||||
system_wait_cv(cancel_lock, cancel_cv);
|
||||
}while (thread->cancel == 1);
|
||||
system_acquire_lock(FRAME_LOCK);
|
||||
|
||||
system_release_lock(cancel_lock);
|
||||
}
|
||||
}
|
||||
cancel_job(group, queue, job_id);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Check_Cancel_Sig(system_check_cancel){
|
||||
b32 result = 0;
|
||||
|
||||
Thread_Group *group = win32vars.groups + thread->group_id;
|
||||
i32 thread_index = thread->id - 1;
|
||||
i32 cancel_lock = group->cancel_lock0 + thread_index;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
if (thread->cancel){
|
||||
result = 1;
|
||||
}
|
||||
system_release_lock(cancel_lock);
|
||||
|
||||
b32 result = check_cancel_status(group, thread);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
|
||||
system_acquire_lock(CANCEL_LOCK0 + memory->id - 1);
|
||||
void *old_data = memory->data;
|
||||
i32 old_size = memory->size;
|
||||
i32 new_size = l_round_up_i32(memory->size*2, KB(4));
|
||||
memory->data = system_memory_allocate(new_size);
|
||||
memory->size = new_size;
|
||||
if (old_data){
|
||||
memcpy(memory->data, old_data, old_size);
|
||||
system_memory_free(old_data, 0);
|
||||
}
|
||||
system_release_lock(CANCEL_LOCK0 + memory->id - 1);
|
||||
grow_thread_memory(memory);
|
||||
}
|
||||
|
||||
#if FRED_INTERNAL
|
||||
internal void
|
||||
INTERNAL_get_thread_states(Thread_Group_ID id, b8 *running, i32 *pending){
|
||||
internal
|
||||
INTERNAL_Sys_Get_Thread_States_Sig(system_internal_get_thread_states){
|
||||
Thread_Group *group = win32vars.groups + id;
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
Work_Queue *queue = win32vars.queues + id;
|
||||
u32 write = queue->write_position;
|
||||
u32 read = queue->read_position;
|
||||
if (write < read){
|
||||
write += QUEUE_WRAP;
|
||||
dbg_get_thread_states(group, queue, running, pending);
|
||||
}
|
||||
*pending = (i32)(write - read) + source_queue->count - source_queue->skip;
|
||||
|
||||
for (i32 i = 0; i < group->count; ++i){
|
||||
running[i] = (group->threads[i].running != 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// Coroutines
|
||||
|
@ -1309,54 +1220,11 @@ Win32LoadAppCode(){
|
|||
#include "4ed_font_data.h"
|
||||
#include "4ed_system_shared.cpp"
|
||||
|
||||
#include "4ed_link_system_functions.cpp"
|
||||
|
||||
internal void
|
||||
Win32LoadSystemCode(){
|
||||
win32vars.system.set_file_list = system_set_file_list;
|
||||
win32vars.system.get_canonical = system_get_canonical;
|
||||
win32vars.system.add_listener = system_add_listener;
|
||||
win32vars.system.remove_listener = system_remove_listener;
|
||||
win32vars.system.get_file_change = system_get_file_change;
|
||||
win32vars.system.load_handle = system_load_handle;
|
||||
win32vars.system.load_size = system_load_size;
|
||||
win32vars.system.load_file = system_load_file;
|
||||
win32vars.system.load_close = system_load_close;
|
||||
win32vars.system.save_file = system_save_file;
|
||||
|
||||
win32vars.system.now_time = system_now_time;
|
||||
|
||||
win32vars.system.post_clipboard = system_post_clipboard;
|
||||
|
||||
win32vars.system.create_coroutine = system_create_coroutine;
|
||||
win32vars.system.launch_coroutine = system_launch_coroutine;
|
||||
win32vars.system.resume_coroutine = system_resume_coroutine;
|
||||
win32vars.system.yield_coroutine = system_yield_coroutine;
|
||||
|
||||
win32vars.system.cli_call = system_cli_call;
|
||||
win32vars.system.cli_begin_update = system_cli_begin_update;
|
||||
win32vars.system.cli_update_step = system_cli_update_step;
|
||||
win32vars.system.cli_end_update = system_cli_end_update;
|
||||
|
||||
win32vars.system.post_job = system_post_job;
|
||||
win32vars.system.cancel_job = system_cancel_job;
|
||||
win32vars.system.check_cancel = system_check_cancel;
|
||||
win32vars.system.grow_thread_memory = system_grow_thread_memory;
|
||||
win32vars.system.acquire_lock = system_acquire_lock;
|
||||
win32vars.system.release_lock = system_release_lock;
|
||||
|
||||
win32vars.system.memory_allocate = system_memory_allocate;
|
||||
win32vars.system.memory_set_protection = system_memory_set_protection;
|
||||
win32vars.system.memory_free = system_memory_free;
|
||||
win32vars.system.file_exists = system_file_exists;
|
||||
win32vars.system.directory_cd = system_directory_cd;
|
||||
win32vars.system.get_4ed_path = system_get_4ed_path;
|
||||
win32vars.system.toggle_fullscreen = system_toggle_fullscreen;
|
||||
win32vars.system.is_fullscreen = system_is_fullscreen;win32vars.system.show_mouse_cursor = system_show_mouse_cursor;
|
||||
win32vars.system.send_exit_signal = system_send_exit_signal;
|
||||
|
||||
win32vars.system.log = system_log;
|
||||
#if FRED_INTERNAL
|
||||
win32vars.system.internal_get_thread_states = INTERNAL_get_thread_states;
|
||||
#endif
|
||||
link_system_code(&win32vars.system);
|
||||
}
|
||||
|
||||
internal void
|
||||
|
@ -1859,11 +1727,11 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
//
|
||||
|
||||
for (i32 i = 0; i < LOCK_COUNT; ++i){
|
||||
InitializeCriticalSection(&win32vars.locks[i]);
|
||||
InitializeCriticalSection(&win32vars.locks[i].crit);
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < CV_COUNT; ++i){
|
||||
InitializeConditionVariable(&win32vars.condition_vars[i]);
|
||||
InitializeConditionVariable(&win32vars.condition_vars[i].cv);
|
||||
}
|
||||
|
||||
Thread_Context background[4];
|
||||
|
|
Loading…
Reference in New Issue