zipped a lot of linux and windows code together related to multithreading and the system code linking

This commit is contained in:
Allen Webster 2017-07-18 12:41:40 -04:00
parent c97e82524e
commit 122cecb6ff
4 changed files with 314 additions and 420 deletions

View File

@ -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

View File

@ -92,7 +92,7 @@ get_work_queue_available_space(i32 write, i32 read){
#define UNBOUNDED_SKIP_MAX 128 #define UNBOUNDED_SKIP_MAX 128
internal i32 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 // 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. // 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 // 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; semaphore_release_count = thread_count;
} }
for (i32 i = 0; i < semaphore_release_count; ++i){
system_release_semaphore(queue->semaphore);
}
return(semaphore_release_count); 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 // BOTTOM

View File

@ -133,6 +133,14 @@ struct Thread_Group{
i32 cancel_cv0; i32 cancel_cv0;
}; };
struct Mutex{
pthread_mutex_t crit;
};
struct Condition_Variable{
pthread_cond_t cv;
};
struct Linux_Vars{ struct Linux_Vars{
Display *XDisplay; Display *XDisplay;
Window XWindow; Window XWindow;
@ -185,8 +193,8 @@ struct Linux_Vars{
Thread_Memory *thread_memory; Thread_Memory *thread_memory;
Thread_Group groups[THREAD_GROUP_COUNT]; Thread_Group groups[THREAD_GROUP_COUNT];
Work_Queue queues[THREAD_GROUP_COUNT]; Work_Queue queues[THREAD_GROUP_COUNT];
pthread_mutex_t locks[LOCK_COUNT]; Mutex locks[LOCK_COUNT];
pthread_cond_t conds[8]; Condition_Variable conds[8];
sem_t thread_semaphore; sem_t thread_semaphore;
i32 dpi_x, dpi_y; i32 dpi_x, dpi_y;
@ -483,14 +491,24 @@ Sys_CLI_End_Update_Sig(system_cli_end_update){
// Threads // 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 internal
Sys_Acquire_Lock_Sig(system_acquire_lock){ Sys_Acquire_Lock_Sig(system_acquire_lock){
pthread_mutex_lock(linuxvars.locks + id); system_internal_acquire_lock(&linuxvars.locks[id]);
} }
internal internal
Sys_Release_Lock_Sig(system_release_lock){ Sys_Release_Lock_Sig(system_release_lock){
pthread_mutex_unlock(linuxvars.locks + id); system_internal_release_lock(&linuxvars.locks[id]);
} }
internal void internal void
@ -514,6 +532,11 @@ system_wait_on(Plat_Handle handle){
sem_wait(LinuxHandleToSem(handle)); sem_wait(LinuxHandleToSem(handle));
} }
internal void
system_release_semaphore(Plat_Handle handle){
sem_post(LinuxHandleToSem(handle));
}
#include "4ed_work_queues.cpp" #include "4ed_work_queues.cpp"
internal void* internal void*
@ -528,22 +551,6 @@ JobThreadProc(void* lpParameter){
return(0); 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. // Note(allen): post_job puts the job on the unbounded queue.
// The unbounded queue is entirely managed by the main thread. // The unbounded queue is entirely managed by the main thread.
// The thread safe queue is bounded in size so the unbounded // The thread safe queue is bounded in size so the unbounded
@ -551,152 +558,36 @@ flush_thread_group(i32 group_id){
internal internal
Sys_Post_Job_Sig(system_post_job){ Sys_Post_Job_Sig(system_post_job){
Thread_Group *group = linuxvars.groups + group_id; 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; 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); return(result);
} }
internal internal
Sys_Cancel_Job_Sig(system_cancel_job){ Sys_Cancel_Job_Sig(system_cancel_job){
Thread_Group *group = linuxvars.groups + group_id; 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; Work_Queue *queue = linuxvars.queues + group_id;
Full_Job_Data *job = queue->jobs + (job_id % QUEUE_WRAP); cancel_job(group, queue, job_id);
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);
}
}
} }
internal internal
Sys_Check_Cancel_Sig(system_check_cancel){ Sys_Check_Cancel_Sig(system_check_cancel){
b32 result = false;
Thread_Group *group = linuxvars.groups + thread->group_id; Thread_Group *group = linuxvars.groups + thread->group_id;
i32 thread_index = thread->id - 1; b32 result = check_cancel_status(group, thread);
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); return(result);
} }
internal internal
Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){ Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
system_acquire_lock(CANCEL_LOCK0 + memory->id - 1); grow_thread_memory(memory);
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);
} }
//
// Debug
//
#if FRED_INTERNAL
#if defined(OLD_JOB_QUEUE)
internal internal
INTERNAL_Sys_Get_Thread_States_Sig(internal_get_thread_states){ INTERNAL_Sys_Get_Thread_States_Sig(system_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);
Thread_Group *group = linuxvars.groups + id; 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; Work_Queue *queue = linuxvars.queues + id;
u32 write = queue->write_position; dbg_get_thread_states(group, queue, running, pending);
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);
}
} }
#endif
#endif
// //
// Linux rendering/font system functions // Linux rendering/font system functions
@ -738,63 +629,11 @@ LinuxLoadAppCode(String* base_dir){
return(result); return(result);
} }
#include "4ed_link_system_functions.cpp"
internal void internal void
LinuxLoadSystemCode(){ LinuxLoadSystemCode(){
// files link_system_code(&linuxvars.system);
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
} }
internal void internal void

View File

@ -86,6 +86,55 @@
#define WM_4coder_ANIMATE (WM_USER + 0) #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{ struct Thread_Context{
u32 job_id; u32 job_id;
b32 running; b32 running;
@ -108,53 +157,12 @@ struct Thread_Group{
i32 cancel_cv0; i32 cancel_cv0;
}; };
struct Control_Keys{ struct Mutex{
b8 l_ctrl; CRITICAL_SECTION crit;
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];
}; };
typedef struct Win32_Input_Chunk{ struct Condition_Variable{
Win32_Input_Chunk_Transient trans; CONDITION_VARIABLE cv;
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 Win32_Vars{ struct Win32_Vars{
@ -165,16 +173,15 @@ struct Win32_Vars{
HMODULE custom; HMODULE custom;
Plat_Settings settings; Plat_Settings settings;
Thread_Memory *thread_memory;
Work_Queue queues[THREAD_GROUP_COUNT]; Work_Queue queues[THREAD_GROUP_COUNT];
Thread_Group groups[THREAD_GROUP_COUNT]; Thread_Group groups[THREAD_GROUP_COUNT];
CRITICAL_SECTION locks[LOCK_COUNT]; Mutex locks[LOCK_COUNT];
CONDITION_VARIABLE condition_vars[CV_COUNT]; Condition_Variable condition_vars[CV_COUNT];
Thread_Memory *thread_memory;
Win32_Coroutine coroutine_data[18]; Win32_Coroutine coroutine_data[18];
Win32_Coroutine *coroutine_free; Win32_Coroutine *coroutine_free;
Win32_Input_Chunk input_chunk; Win32_Input_Chunk input_chunk;
b32 lctrl_lalt_is_altgr; b32 lctrl_lalt_is_altgr;
b32 got_useful_event; b32 got_useful_event;
@ -336,25 +343,34 @@ Sys_Memory_Free_Sig(system_memory_free){
// Multithreading // Multithreading
// //
internal void
system_internal_acquire_lock(Mutex *m){
EnterCriticalSection(&m->crit);
}
internal void
system_internal_release_lock(Mutex *m){
LeaveCriticalSection(&m->crit);
}
internal internal
Sys_Acquire_Lock_Sig(system_acquire_lock){ Sys_Acquire_Lock_Sig(system_acquire_lock){
EnterCriticalSection(&win32vars.locks[id]); system_internal_acquire_lock(&win32vars.locks[id]);
} }
internal internal
Sys_Release_Lock_Sig(system_release_lock){ Sys_Release_Lock_Sig(system_release_lock){
LeaveCriticalSection(&win32vars.locks[id]); system_internal_release_lock(&win32vars.locks[id]);
} }
internal void internal void
system_wait_cv(i32 crit_id, i32 cv_id){ 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 internal void
system_signal_cv(i32 crit_id, i32 cv_id){ system_signal_cv(i32 crit_id, i32 cv_id){
AllowLocal(crit_id); WakeConditionVariable(&win32vars.condition_vars[cv_id].cv);
WakeConditionVariable(win32vars.condition_vars + cv_id);
} }
internal void internal void
@ -367,6 +383,11 @@ system_wait_on(Plat_Handle handle){
WaitForSingleObject(Win32Handle(handle), INFINITE); WaitForSingleObject(Win32Handle(handle), INFINITE);
} }
internal void
system_release_semaphore(Plat_Handle handle){
ReleaseSemaphore(Win32Handle(handle), 1, 0);
}
#include "4ed_work_queues.cpp" #include "4ed_work_queues.cpp"
internal DWORD CALL_CONVENTION internal DWORD CALL_CONVENTION
@ -381,14 +402,6 @@ JobThreadProc(LPVOID lpParameter){
return(0); 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 internal void
flush_thread_group(i32 group_id){ flush_thread_group(i32 group_id){
Thread_Group *group = win32vars.groups + 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); 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 internal
Sys_Post_Job_Sig(system_post_job){ Sys_Post_Job_Sig(system_post_job){
Thread_Group *group = win32vars.groups + group_id; 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; 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); return(result);
} }
internal internal
Sys_Cancel_Job_Sig(system_cancel_job){ Sys_Cancel_Job_Sig(system_cancel_job){
Thread_Group *group = win32vars.groups + group_id; 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; Work_Queue *queue = win32vars.queues + group_id;
Full_Job_Data *job = queue->jobs + (job_id % QUEUE_WRAP); cancel_job(group, queue, job_id);
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);
}
}
} }
internal internal
Sys_Check_Cancel_Sig(system_check_cancel){ Sys_Check_Cancel_Sig(system_check_cancel){
b32 result = 0;
Thread_Group *group = win32vars.groups + thread->group_id; Thread_Group *group = win32vars.groups + thread->group_id;
i32 thread_index = thread->id - 1; b32 result = check_cancel_status(group, thread);
i32 cancel_lock = group->cancel_lock0 + thread_index;
system_acquire_lock(cancel_lock);
if (thread->cancel){
result = 1;
}
system_release_lock(cancel_lock);
return(result); return(result);
} }
internal internal
Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){ Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
system_acquire_lock(CANCEL_LOCK0 + memory->id - 1); grow_thread_memory(memory);
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);
} }
#if FRED_INTERNAL internal
internal void INTERNAL_Sys_Get_Thread_States_Sig(system_internal_get_thread_states){
INTERNAL_get_thread_states(Thread_Group_ID id, b8 *running, i32 *pending){
Thread_Group *group = win32vars.groups + id; Thread_Group *group = win32vars.groups + id;
Unbounded_Work_Queue *source_queue = &group->queue;
Work_Queue *queue = win32vars.queues + id; Work_Queue *queue = win32vars.queues + id;
u32 write = queue->write_position; dbg_get_thread_states(group, queue, running, pending);
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);
}
} }
#endif
// //
// Coroutines // Coroutines
@ -1309,54 +1220,11 @@ Win32LoadAppCode(){
#include "4ed_font_data.h" #include "4ed_font_data.h"
#include "4ed_system_shared.cpp" #include "4ed_system_shared.cpp"
#include "4ed_link_system_functions.cpp"
internal void internal void
Win32LoadSystemCode(){ Win32LoadSystemCode(){
win32vars.system.set_file_list = system_set_file_list; link_system_code(&win32vars.system);
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
} }
internal void internal void
@ -1859,11 +1727,11 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
// //
for (i32 i = 0; i < LOCK_COUNT; ++i){ 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){ for (i32 i = 0; i < CV_COUNT; ++i){
InitializeConditionVariable(&win32vars.condition_vars[i]); InitializeConditionVariable(&win32vars.condition_vars[i].cv);
} }
Thread_Context background[4]; Thread_Context background[4];