Eliminated old file change notification system; replaced OS layer job system with OS layer thread wrappers; moved coroutine implementation from OS layer to core
parent
d4db77b3fb
commit
2f6b9744bd
54
4ed.cpp
54
4ed.cpp
|
@ -26,16 +26,16 @@ restore_state(Application_Links *app, App_Coroutine_State state){
|
|||
app->type_coroutine = state.type;
|
||||
}
|
||||
|
||||
internal Coroutine_Head*
|
||||
app_coroutine_handle_request(Models *models, Coroutine_Head *co, u32 *vals){
|
||||
Coroutine_Head *result = 0;
|
||||
internal Coroutine*
|
||||
app_coroutine_handle_request(Models *models, Coroutine *co, u32 *vals){
|
||||
Coroutine *result = 0;
|
||||
System_Functions *system = models->system;
|
||||
switch (vals[2]){
|
||||
case AppCoroutineRequest_NewFontFace:
|
||||
{
|
||||
Face_Description *description = ((Face_Description**)vals)[0];
|
||||
Face_ID face_id = font_set_new_face(&models->font_set, description);
|
||||
result = system->resume_coroutine(co, &face_id, vals);
|
||||
result = coroutine_run(&models->coroutines, co, &face_id, vals);
|
||||
}break;
|
||||
|
||||
case AppCoroutineRequest_ModifyFace:
|
||||
|
@ -43,36 +43,22 @@ app_coroutine_handle_request(Models *models, Coroutine_Head *co, u32 *vals){
|
|||
Face_Description *description = ((Face_Description**)vals)[0];
|
||||
Face_ID face_id = ((Face_ID*)vals)[3];
|
||||
b32 success = alter_font_and_update_files(system, models, face_id, description);
|
||||
result = system->resume_coroutine(co, &success, vals);
|
||||
result = coroutine_run(&models->coroutines, co, &success, vals);
|
||||
}break;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal Coroutine_Head*
|
||||
app_launch_coroutine(System_Functions *system, Application_Links *app, Coroutine_Type type, Coroutine_Head *co, void *in, u32 *out){
|
||||
internal Coroutine*
|
||||
app_coroutine_run(Models *models, App_Coroutine_Purpose purpose, Coroutine *co, void *in, u32 *out){
|
||||
Application_Links *app = &models->app_links;
|
||||
App_Coroutine_State prev_state = get_state(app);
|
||||
app->current_coroutine = co;
|
||||
app->type_coroutine = type;
|
||||
app->type_coroutine = purpose;
|
||||
u32 coroutine_out[4] = {};
|
||||
Coroutine_Head *result = system->launch_coroutine(co, in, coroutine_out);
|
||||
Coroutine *result = coroutine_run(&models->coroutines, co, in, coroutine_out);
|
||||
for (;result != 0 && coroutine_out[2] != 0;){
|
||||
result = app_coroutine_handle_request((Models*)app->cmd_context, result, coroutine_out);
|
||||
}
|
||||
block_copy(out, coroutine_out, sizeof(*out)*2);
|
||||
restore_state(app, prev_state);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal Coroutine_Head*
|
||||
app_resume_coroutine(System_Functions *system, Application_Links *app, Coroutine_Type type, Coroutine_Head *co, void *in, u32 *out){
|
||||
App_Coroutine_State prev_state = get_state(app);
|
||||
app->current_coroutine = co;
|
||||
app->type_coroutine = type;
|
||||
u32 coroutine_out[4] = {};
|
||||
Coroutine_Head *result = system->resume_coroutine(co, in, coroutine_out);
|
||||
for (;result != 0 && coroutine_out[2] != 0;){
|
||||
result = app_coroutine_handle_request((Models*)app->cmd_context, co, coroutine_out);
|
||||
result = app_coroutine_handle_request(models, result, coroutine_out);
|
||||
}
|
||||
block_copy(out, coroutine_out, sizeof(*out)*2);
|
||||
restore_state(app, prev_state);
|
||||
|
@ -475,7 +461,7 @@ interpret_binding_buffer(Models *models, void *buffer, i32 size){
|
|||
#include "4ed_api_implementation.cpp"
|
||||
|
||||
internal void
|
||||
command_caller(Coroutine_Head *coroutine){
|
||||
command_caller(Coroutine *coroutine){
|
||||
Command_In *cmd_in = (Command_In*)coroutine->in;
|
||||
Models *models = cmd_in->models;
|
||||
if (models->command_caller != 0){
|
||||
|
@ -795,7 +781,7 @@ force_abort_coroutine(System_Functions *system, Models *models, View *view){
|
|||
User_Input user_in = {};
|
||||
user_in.abort = true;
|
||||
for (u32 j = 0; j < 10 && models->command_coroutine != 0; ++j){
|
||||
models->command_coroutine = app_resume_coroutine(system, &models->app_links, Co_Command, models->command_coroutine, &user_in, models->command_coroutine_flags);
|
||||
models->command_coroutine = app_coroutine_run(models, Co_Command, models->command_coroutine, &user_in, models->command_coroutine_flags);
|
||||
}
|
||||
if (models->command_coroutine != 0){
|
||||
// TODO(allen): post grave warning
|
||||
|
@ -813,14 +799,14 @@ launch_command_via_event(System_Functions *system, Models *models, View *view, K
|
|||
|
||||
if (cmd_bind.custom != 0){
|
||||
Assert(models->command_coroutine == 0);
|
||||
Coroutine_Head *command_coroutine = system->create_coroutine(command_caller);
|
||||
Coroutine *command_coroutine = coroutine_create(&models->coroutines, command_caller);
|
||||
models->command_coroutine = command_coroutine;
|
||||
|
||||
Command_In cmd_in = {};
|
||||
cmd_in.models = models;
|
||||
cmd_in.bind = cmd_bind;
|
||||
|
||||
models->command_coroutine = app_launch_coroutine(system, &models->app_links, Co_Command, models->command_coroutine, &cmd_in, models->command_coroutine_flags);
|
||||
models->command_coroutine = app_coroutine_run(models, Co_Command, models->command_coroutine, &cmd_in, models->command_coroutine_flags);
|
||||
|
||||
models->prev_command = cmd_bind;
|
||||
if (event.keycode != key_animate){
|
||||
|
@ -863,6 +849,9 @@ App_Init_Sig(app_init){
|
|||
|
||||
Arena *arena = &models->mem.arena;
|
||||
|
||||
// NOTE(allen): coroutines
|
||||
coroutine_system_init(system, &models->coroutines);
|
||||
|
||||
// NOTE(allen): font set
|
||||
font_set_init(system, &models->font_set);
|
||||
|
||||
|
@ -1010,6 +999,7 @@ App_Step_Sig(app_step){
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// NOTE(allen): check files are up to date
|
||||
{
|
||||
b32 mem_too_small = 0;
|
||||
|
@ -1051,6 +1041,7 @@ App_Step_Sig(app_step){
|
|||
|
||||
end_temp(temp);
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOTE(allen): reorganizing panels on screen
|
||||
Vec2_i32 prev_dim = layout_get_root_size(&models->layout);
|
||||
|
@ -1262,12 +1253,11 @@ App_Step_Sig(app_step){
|
|||
|
||||
case EventConsume_Command:
|
||||
{
|
||||
|
||||
// NOTE(allen): update command coroutine
|
||||
if (models->command_coroutine != 0){
|
||||
models->key = *key_ptr;
|
||||
|
||||
Coroutine_Head *command_coroutine = models->command_coroutine;
|
||||
Coroutine *command_coroutine = models->command_coroutine;
|
||||
u32 abort_flags = models->command_coroutine_flags[1];
|
||||
u32 get_flags = models->command_coroutine_flags[0] | abort_flags;
|
||||
|
||||
|
@ -1280,7 +1270,7 @@ App_Step_Sig(app_step){
|
|||
user_in.key = *key_ptr;
|
||||
user_in.command.command = cmd_bind.custom;
|
||||
user_in.abort = ((abort_flags & event_flags) != 0);
|
||||
models->command_coroutine = app_resume_coroutine(system, &models->app_links, Co_Command, command_coroutine, &user_in, models->command_coroutine_flags);
|
||||
models->command_coroutine = app_coroutine_run(models, Co_Command, command_coroutine, &user_in, models->command_coroutine_flags);
|
||||
|
||||
if (user_in.key.keycode != key_animate){
|
||||
models->animate_next_frame = true;
|
||||
|
|
|
@ -30,7 +30,7 @@ api_check_buffer(Editing_File *file){
|
|||
|
||||
internal b32
|
||||
api_check_buffer_and_tokens(Editing_File *file){
|
||||
return(api_check_buffer(file) && file->state.token_array.tokens != 0 && file->state.tokens_complete);
|
||||
return(api_check_buffer(file) && file->state.token_array.tokens != 0);
|
||||
}
|
||||
|
||||
internal b32
|
||||
|
@ -856,14 +856,9 @@ DOC_SEE(Buffer_Setting_ID)
|
|||
if (!file->settings.tokens_exist){
|
||||
file_first_lex_serial(system, models, file);
|
||||
}
|
||||
if (!file->state.still_lexing){
|
||||
file->settings.virtual_white = true;
|
||||
full_remeasure = true;
|
||||
}
|
||||
else{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (file->settings.virtual_white){
|
||||
|
@ -2583,11 +2578,11 @@ DOC_SEE(User_Input)
|
|||
System_Functions *system = models->system;
|
||||
User_Input result = {};
|
||||
if (app->type_coroutine == Co_Command){
|
||||
Coroutine_Head *coroutine = (Coroutine_Head*)app->current_coroutine;
|
||||
Coroutine *coroutine = (Coroutine*)app->current_coroutine;
|
||||
Assert(coroutine != 0);
|
||||
((u32*)coroutine->out)[0] = get_type;
|
||||
((u32*)coroutine->out)[1] = abort_type;
|
||||
system->yield_coroutine(coroutine);
|
||||
coroutine_yield(coroutine);
|
||||
result = *(User_Input*)coroutine->in;
|
||||
}
|
||||
return(result);
|
||||
|
@ -3027,11 +3022,11 @@ DOC_SEE(Face_Description)
|
|||
Face_ID result = 0;
|
||||
if (is_running_coroutine(app)){
|
||||
System_Functions *system = models->system;
|
||||
Coroutine_Head *coroutine = (Coroutine_Head*)app->current_coroutine;
|
||||
Coroutine *coroutine = (Coroutine*)app->current_coroutine;
|
||||
Assert(coroutine != 0);
|
||||
((Face_Description**)coroutine->out)[0] = description;
|
||||
((u32*)coroutine->out)[2] = AppCoroutineRequest_NewFontFace;
|
||||
system->yield_coroutine(coroutine);
|
||||
coroutine_yield(coroutine);
|
||||
result = *(Face_ID*)(coroutine->in);
|
||||
}
|
||||
else{
|
||||
|
@ -3058,12 +3053,12 @@ DOC_SEE(try_create_new_face)
|
|||
b32 result = false;
|
||||
if (is_running_coroutine(app)){
|
||||
System_Functions *system = models->system;
|
||||
Coroutine_Head *coroutine = (Coroutine_Head*)app->current_coroutine;
|
||||
Coroutine *coroutine = (Coroutine*)app->current_coroutine;
|
||||
Assert(coroutine != 0);
|
||||
((Face_Description**)coroutine->out)[0] = description;
|
||||
((u32*)coroutine->out)[2] = AppCoroutineRequest_ModifyFace;
|
||||
((u32*)coroutine->out)[3] = id;
|
||||
system->yield_coroutine(coroutine);
|
||||
coroutine_yield(coroutine);
|
||||
result = *(b32*)(coroutine->in);
|
||||
}
|
||||
else{
|
||||
|
|
|
@ -38,7 +38,8 @@ struct Models{
|
|||
Mapping mapping;
|
||||
Command_Binding prev_command;
|
||||
|
||||
Coroutine_Head *command_coroutine;
|
||||
Coroutine_Group coroutines;
|
||||
Coroutine *command_coroutine;
|
||||
u32 command_coroutine_flags[2];
|
||||
|
||||
Child_Process_Container child_processes;
|
||||
|
@ -176,7 +177,8 @@ struct App_Vars{
|
|||
App_State state;
|
||||
};
|
||||
|
||||
enum Coroutine_Type{
|
||||
typedef i32 App_Coroutine_Purpose;
|
||||
enum{
|
||||
Co_View,
|
||||
Co_Command,
|
||||
};
|
||||
|
|
|
@ -44,6 +44,7 @@ struct Mem_Options{
|
|||
#include "4ed_render_target.h"
|
||||
#include "4ed.h"
|
||||
#include "4ed_buffer_model.h"
|
||||
#include "4ed_coroutine.h"
|
||||
|
||||
#define FCPP_FORBID_MALLOC
|
||||
#include "4coder_lib/4cpp_lexer.h"
|
||||
|
@ -72,6 +73,7 @@ struct Mem_Options{
|
|||
#include "4ed_font_set.h"
|
||||
#include "4ed_app_models.h"
|
||||
|
||||
#include "4ed_coroutine.cpp"
|
||||
#include "4ed_mem.cpp"
|
||||
#include "4ed_ptr_check.cpp"
|
||||
#include "4ed_memory_bank.cpp"
|
||||
|
|
|
@ -483,7 +483,7 @@ file_measure_wraps(System_Functions *system, Mem_Options *mem, Editing_File *fil
|
|||
Potential_Wrap_Indent_Pair *potential_marks = 0;
|
||||
i32 max_wrap_indent_mark = Million(1);
|
||||
|
||||
if (params.virtual_white && file->state.tokens_complete && !file->state.still_lexing){
|
||||
if (params.virtual_white && file->state.token_array.tokens != 0){
|
||||
wrap_state_init(&wrap_state, file, face);
|
||||
use_tokens = true;
|
||||
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 19.07.2017
|
||||
*
|
||||
* Coroutine implementation from thread+mutex+cv
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
internal void
|
||||
coroutine__pass_control(Coroutine *me, Coroutine *other,
|
||||
Coroutine_State my_new_state, Coroutine_Pass_Control control){
|
||||
Assert(me->state == CoroutineState_Active);
|
||||
Assert(me->sys == other->sys);
|
||||
|
||||
System_Functions *system = me->system;
|
||||
|
||||
me->state = my_new_state;
|
||||
other->state = CoroutineState_Active;
|
||||
me->sys->active = other;
|
||||
system->condition_variable_signal(other->cv);
|
||||
if (control == CoroutinePassControl_BlockMe){
|
||||
for (;me->state != CoroutineState_Active;){
|
||||
system->condition_variable_wait(me->cv, me->sys->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_main(void *ptr){
|
||||
Coroutine *me = (Coroutine*)ptr;
|
||||
System_Functions *system = me->system;
|
||||
|
||||
// NOTE(allen): Init handshake
|
||||
Assert(me->state == CoroutineState_Dead);
|
||||
system->mutex_acquire(me->sys->lock);
|
||||
me->sys->did_init = true;
|
||||
system->condition_variable_signal(me->sys->init_cv);
|
||||
|
||||
for (;;){
|
||||
// NOTE(allen): Wait until someone wakes us up, then go into our procedure.
|
||||
for (;me->state != CoroutineState_Active;){
|
||||
system->condition_variable_wait(me->cv, me->sys->lock);
|
||||
}
|
||||
Assert(me->type != CoroutineType_Root);
|
||||
Assert(me->yield_ctx != 0);
|
||||
Assert(me->function != 0);
|
||||
|
||||
me->function(me);
|
||||
|
||||
// NOTE(allen): Wake up the caller and set this coroutine back to being dead.
|
||||
Coroutine *other = me->yield_ctx;
|
||||
Assert(other != 0);
|
||||
Assert(other->state == CoroutineState_Waiting);
|
||||
|
||||
coroutine__pass_control(me, other, CoroutineState_Dead, CoroutinePassControl_ExitMe);
|
||||
me->function = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_sub_init(Coroutine *co, Coroutine_Group *sys){
|
||||
System_Functions *system = sys->system;
|
||||
block_zero_struct(co);
|
||||
co->system = system;
|
||||
co->sys = sys;
|
||||
co->state = CoroutineState_Dead;
|
||||
co->type = CoroutineType_Sub;
|
||||
co->cv = system->condition_variable_make();
|
||||
sys->did_init = false;
|
||||
co->thread = system->thread_launch(coroutine_main, co);
|
||||
for (;!sys->did_init;){
|
||||
system->condition_variable_wait(sys->init_cv, sys->lock);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_system_init(System_Functions *system, Coroutine_Group *sys){
|
||||
sys->arena = make_arena_system(system);
|
||||
sys->system = system;
|
||||
|
||||
Coroutine *root = &sys->root;
|
||||
|
||||
sys->lock = system->mutex_make();
|
||||
sys->init_cv = system->condition_variable_make();
|
||||
sys->active = root;
|
||||
|
||||
block_zero_struct(root);
|
||||
root->system = system;
|
||||
root->sys = sys;
|
||||
root->state = CoroutineState_Active;
|
||||
root->type = CoroutineType_Root;
|
||||
root->cv = system->condition_variable_make();
|
||||
|
||||
sys->unused = 0;
|
||||
|
||||
system->mutex_acquire(sys->lock);
|
||||
}
|
||||
|
||||
internal Coroutine*
|
||||
coroutine_system_alloc(Coroutine_Group *sys){
|
||||
Coroutine *result = sys->unused;
|
||||
if (result != 0){
|
||||
sll_stack_pop(sys->unused);
|
||||
}
|
||||
else{
|
||||
result = push_array(&sys->arena, Coroutine, 1);
|
||||
coroutine_sub_init(result, sys);
|
||||
}
|
||||
result->next = 0;
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_system_free(Coroutine_Group *sys, Coroutine *coroutine){
|
||||
sll_stack_push(sys->unused, coroutine);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal Coroutine*
|
||||
coroutine_create(Coroutine_Group *coroutines, Coroutine_Function *func){
|
||||
Coroutine *result = coroutine_system_alloc(coroutines);
|
||||
Assert(result->state == CoroutineState_Dead);
|
||||
result->function = func;
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal Coroutine*
|
||||
coroutine_run(Coroutine_Group *sys, Coroutine *other, void *in, void *out_arena){
|
||||
other->in = in;
|
||||
other->out = out;
|
||||
|
||||
Coroutine *me = other->sys->active;
|
||||
Assert(me != 0);
|
||||
Assert(me->sys == other->sys);
|
||||
Assert(other->state == CoroutineState_Dead || other->state == CoroutineState_Inactive);
|
||||
other->yield_ctx = me;
|
||||
coroutine__pass_control(me, other, CoroutineState_Waiting, CoroutinePassControl_BlockMe);
|
||||
Assert(me == other->sys->active);
|
||||
|
||||
Coroutine *result = other;
|
||||
if (other->state == CoroutineState_Dead){
|
||||
coroutine_system_free(sys, other);
|
||||
result = 0;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_yield(Coroutine *me){
|
||||
Coroutine *other = me->yield_ctx;
|
||||
Assert(other != 0);
|
||||
Assert(me->sys == other->sys);
|
||||
Assert(other->state == CoroutineState_Waiting);
|
||||
coroutine__pass_control(me, other, CoroutineState_Inactive, CoroutinePassControl_BlockMe);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 03.08.2019
|
||||
*
|
||||
* Coroutine implementation from thread+mutex+cv
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#if !defined(FRED_COROUTINE_H)
|
||||
#define FRED_COROUTINE_H
|
||||
|
||||
typedef void Coroutine_Function(struct Coroutine *head);
|
||||
|
||||
typedef u32 Coroutine_State;
|
||||
enum{
|
||||
CoroutineState_Dead,
|
||||
CoroutineState_Active,
|
||||
CoroutineState_Inactive,
|
||||
CoroutineState_Waiting,
|
||||
};
|
||||
|
||||
typedef u32 Coroutine_Type;
|
||||
enum{
|
||||
CoroutineType_Uninitialized,
|
||||
CoroutineType_Root,
|
||||
CoroutineType_Sub,
|
||||
};
|
||||
|
||||
struct Coroutine{
|
||||
Coroutine *next;
|
||||
void *in;
|
||||
void *out;
|
||||
System_Functions *system;
|
||||
System_Thread thread;
|
||||
System_Condition_Variable cv;
|
||||
struct Coroutine_Group *sys;
|
||||
Coroutine_Function *function;
|
||||
Coroutine *yield_ctx;
|
||||
Coroutine_State state;
|
||||
Coroutine_Type type;
|
||||
};
|
||||
|
||||
struct Coroutine_Group{
|
||||
Arena arena;
|
||||
System_Functions *system;
|
||||
System_Mutex lock;
|
||||
System_Condition_Variable init_cv;
|
||||
b32 did_init;
|
||||
Coroutine *active;
|
||||
Coroutine *unused;
|
||||
Coroutine root;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
typedef i32 Coroutine_Pass_Control;
|
||||
enum{
|
||||
CoroutinePassControl_ExitMe,
|
||||
CoroutinePassControl_BlockMe,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// BOTTOM
|
|
@ -12,14 +12,6 @@
|
|||
internal void
|
||||
edit_pre_state_change(Models *models, Heap *heap, Editing_File *file){
|
||||
System_Functions *system = models->system;
|
||||
if (file->state.still_lexing){
|
||||
system->cancel_job(BACKGROUND_THREADS, file->state.lex_job);
|
||||
if (file->state.swap_array.tokens){
|
||||
heap_free(heap, file->state.swap_array.tokens);
|
||||
file->state.swap_array.tokens = 0;
|
||||
}
|
||||
file->state.still_lexing = 0;
|
||||
}
|
||||
file_add_dirty_flag(file, DirtyState_UnsavedChanges);
|
||||
file_unmark_edit_finished(&models->working_set, file);
|
||||
Layout *layout = &models->layout;
|
||||
|
|
|
@ -485,13 +485,6 @@ file_create_from_string(System_Functions *system, Models *models, Editing_File *
|
|||
|
||||
internal void
|
||||
file_free(System_Functions *system, Heap *heap, Lifetime_Allocator *lifetime_allocator, Working_Set *working_set, Editing_File *file){
|
||||
if (file->state.still_lexing){
|
||||
system->cancel_job(BACKGROUND_THREADS, file->state.lex_job);
|
||||
if (file->state.swap_array.tokens){
|
||||
heap_free(heap, file->state.swap_array.tokens);
|
||||
file->state.swap_array.tokens = 0;
|
||||
}
|
||||
}
|
||||
if (file->state.token_array.tokens){
|
||||
heap_free(heap, file->state.token_array.tokens);
|
||||
}
|
||||
|
@ -522,7 +515,7 @@ file_get_current_record_index(Editing_File *file){
|
|||
|
||||
internal b32
|
||||
file_tokens_are_ready(Editing_File *file){
|
||||
return(file->state.token_array.tokens != 0 && file->state.tokens_complete && !file->state.still_lexing);
|
||||
return(file->state.token_array.tokens != 0);
|
||||
}
|
||||
|
||||
internal Managed_Scope
|
||||
|
|
|
@ -78,11 +78,7 @@ struct Editing_File_State{
|
|||
i32 current_record_index;
|
||||
|
||||
Cpp_Token_Array token_array;
|
||||
Cpp_Token_Array swap_array;
|
||||
u32 lex_job;
|
||||
b8 tokens_complete;
|
||||
b8 still_lexing;
|
||||
b8 in_edit_handler;
|
||||
b32 in_edit_handler;
|
||||
|
||||
Text_Effect paste_effect;
|
||||
|
||||
|
|
332
4ed_file_lex.cpp
332
4ed_file_lex.cpp
|
@ -9,159 +9,13 @@
|
|||
|
||||
// TOP
|
||||
|
||||
// TODO(allen): this needs a rewrite _BAD_
|
||||
internal void
|
||||
job_full_lex(System_Functions *system, Thread_Context *thread, Thread_Memory *memory, void *data[4]){
|
||||
Editing_File *file = (Editing_File*)data[0];
|
||||
Heap *heap = (Heap*)data[1];
|
||||
Models *models = (Models*)data[2];
|
||||
|
||||
Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, memory->data, memory->size);
|
||||
if (!parse_context.valid){
|
||||
return;
|
||||
}
|
||||
|
||||
Gap_Buffer *buffer = &file->state.buffer;
|
||||
i32 text_size = buffer_size(buffer);
|
||||
|
||||
u32 aligned_buffer_size = (text_size + 3)&(~3);
|
||||
|
||||
for (;memory->size < aligned_buffer_size + parse_context.memory_size;){
|
||||
void *old_base = memory->data;
|
||||
system->grow_thread_memory(memory);
|
||||
parse_context_rebase(&parse_context, old_base, memory->data);
|
||||
}
|
||||
|
||||
u8 *data_ptr = (u8*)memory->data;
|
||||
umem data_size = memory->size;
|
||||
data_ptr += parse_context.memory_size;
|
||||
data_size -= parse_context.memory_size;
|
||||
|
||||
Cpp_Token_Array tokens = {};
|
||||
tokens.tokens = (Cpp_Token*)(data_ptr);
|
||||
tokens.max_count = (u32)(data_size / sizeof(Cpp_Token));
|
||||
tokens.count = 0;
|
||||
|
||||
b32 still_lexing = true;
|
||||
|
||||
Cpp_Lex_Data lex = cpp_lex_data_init(file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table);
|
||||
|
||||
String_Const_u8 chunk_space[3];
|
||||
Cursor chunk_cursor = make_cursor(chunk_space, sizeof(chunk_space));
|
||||
String_Const_u8_Array chunks = buffer_get_chunks(&chunk_cursor, buffer);
|
||||
|
||||
i32 chunk_index = 0;
|
||||
|
||||
do{
|
||||
u8 *chunk = chunks.vals[chunk_index].str;
|
||||
umem chunk_size = chunks.vals[chunk_index].size;
|
||||
|
||||
i32 result = cpp_lex_step(&lex, (char*)chunk, (i32)chunk_size, text_size, &tokens, 2048);
|
||||
switch (result){
|
||||
case LexResult_NeedChunk:
|
||||
{
|
||||
++chunk_index;
|
||||
Assert(chunk_index < chunks.count);
|
||||
}break;
|
||||
|
||||
case LexResult_NeedTokenMemory:
|
||||
{
|
||||
if (system->check_cancel(thread)){
|
||||
return;
|
||||
}
|
||||
|
||||
void *old_base = memory->data;
|
||||
system->grow_thread_memory(memory);
|
||||
cpp_rebase_tables(&lex, old_base, memory->data);
|
||||
|
||||
data_ptr = (u8*)memory->data;
|
||||
data_size = memory->size;
|
||||
data_ptr += parse_context.memory_size;
|
||||
data_size -= parse_context.memory_size;
|
||||
tokens.tokens = (Cpp_Token*)(data_ptr);
|
||||
tokens.max_count = (u32)(data_size / sizeof(Cpp_Token));
|
||||
}break;
|
||||
|
||||
case LexResult_HitTokenLimit:
|
||||
{
|
||||
if (system->check_cancel(thread)){
|
||||
return;
|
||||
}
|
||||
}break;
|
||||
|
||||
case LexResult_Finished:
|
||||
{
|
||||
still_lexing = false;
|
||||
}break;
|
||||
}
|
||||
}while(still_lexing);
|
||||
|
||||
i32 new_max = round_up_i32(tokens.count + 1, KB(1));
|
||||
|
||||
system->acquire_lock(FRAME_LOCK);
|
||||
{
|
||||
Assert(file->state.swap_array.tokens == 0);
|
||||
file->state.swap_array.tokens = heap_array(heap, Cpp_Token, new_max);
|
||||
}
|
||||
system->release_lock(FRAME_LOCK);
|
||||
|
||||
u8 *dest = (u8*)file->state.swap_array.tokens;
|
||||
u8 *src = (u8*)tokens.tokens;
|
||||
|
||||
memcpy(dest, src, tokens.count*sizeof(Cpp_Token));
|
||||
|
||||
system->acquire_lock(FRAME_LOCK);
|
||||
{
|
||||
Cpp_Token_Array *file_token_array = &file->state.token_array;
|
||||
file_token_array->count = tokens.count;
|
||||
file_token_array->max_count = new_max;
|
||||
if (file_token_array->tokens){
|
||||
heap_free(heap, file_token_array->tokens);
|
||||
}
|
||||
file_token_array->tokens = file->state.swap_array.tokens;
|
||||
file->state.swap_array.tokens = 0;
|
||||
}
|
||||
file->state.tokens_complete = true;
|
||||
file->state.still_lexing = false;
|
||||
file_mark_edit_finished(&models->working_set, file);
|
||||
system->release_lock(FRAME_LOCK);
|
||||
}
|
||||
|
||||
internal void
|
||||
file_kill_tokens(System_Functions *system, Heap *heap, Editing_File *file){
|
||||
file->settings.tokens_exist = false;
|
||||
if (file->state.still_lexing){
|
||||
system->cancel_job(BACKGROUND_THREADS, file->state.lex_job);
|
||||
if (file->state.swap_array.tokens){
|
||||
heap_free(heap, file->state.swap_array.tokens);
|
||||
file->state.swap_array.tokens = 0;
|
||||
}
|
||||
}
|
||||
if (file->state.token_array.tokens){
|
||||
if (file->state.token_array.tokens != 0){
|
||||
heap_free(heap, file->state.token_array.tokens);
|
||||
}
|
||||
file->state.tokens_complete = false;
|
||||
file->state.token_array = null_cpp_token_array;
|
||||
}
|
||||
|
||||
internal void
|
||||
file_first_lex_parallel(System_Functions *system, Models *models, Editing_File *file){
|
||||
Heap *heap = &models->mem.heap;
|
||||
file->settings.tokens_exist = true;
|
||||
|
||||
if (!file->is_loading && !file->state.still_lexing){
|
||||
Assert(file->state.token_array.tokens == 0);
|
||||
|
||||
file->state.tokens_complete = false;
|
||||
file->state.still_lexing = true;
|
||||
|
||||
Job_Data job;
|
||||
job.callback = job_full_lex;
|
||||
job.data[0] = file;
|
||||
job.data[1] = heap;
|
||||
job.data[2] = models;
|
||||
file->state.lex_job = system->post_job(BACKGROUND_THREADS, job);
|
||||
}
|
||||
block_zero_struct(&file->state.token_array);
|
||||
}
|
||||
|
||||
internal void
|
||||
|
@ -171,8 +25,6 @@ file_first_lex_serial(System_Functions *system, Models *models, Editing_File *fi
|
|||
Heap *heap = &mem->heap;
|
||||
file->settings.tokens_exist = true;
|
||||
|
||||
Assert(!file->state.still_lexing);
|
||||
|
||||
if (file->is_loading == 0){
|
||||
Assert(file->state.token_array.tokens == 0);
|
||||
|
||||
|
@ -200,7 +52,7 @@ file_first_lex_serial(System_Functions *system, Models *models, Editing_File *fi
|
|||
|
||||
i32 chunk_index = 0;
|
||||
|
||||
Cpp_Token_Array *swap_array = &file->state.swap_array;
|
||||
Cpp_Token_Array new_array = {};
|
||||
|
||||
do{
|
||||
u8 *chunk = chunks.vals[chunk_index].str;
|
||||
|
@ -218,22 +70,22 @@ file_first_lex_serial(System_Functions *system, Models *models, Editing_File *fi
|
|||
case LexResult_Finished:
|
||||
case LexResult_NeedTokenMemory:
|
||||
{
|
||||
u32 new_max = round_up_u32(swap_array->count + new_tokens.count + 1, KB(1));
|
||||
if (swap_array->tokens == 0){
|
||||
swap_array->tokens = heap_array(heap, Cpp_Token, new_max);
|
||||
u32 new_max = round_up_u32(new_array.count + new_tokens.count + 1, KB(1));
|
||||
if (new_array.tokens == 0){
|
||||
new_array.tokens = heap_array(heap, Cpp_Token, new_max);
|
||||
}
|
||||
else{
|
||||
u32 old_count = swap_array->count;
|
||||
u32 old_count = new_array.count;
|
||||
Cpp_Token *new_token_mem = heap_array(heap, Cpp_Token, new_max);
|
||||
memcpy(new_token_mem, swap_array->tokens, sizeof(*new_token_mem)*old_count);
|
||||
heap_free(heap, swap_array->tokens);
|
||||
swap_array->tokens = new_token_mem;
|
||||
memcpy(new_token_mem, new_array.tokens, sizeof(*new_token_mem)*old_count);
|
||||
heap_free(heap, new_array.tokens);
|
||||
new_array.tokens = new_token_mem;
|
||||
}
|
||||
swap_array->max_count = new_max;
|
||||
new_array.max_count = new_max;
|
||||
|
||||
Assert(swap_array->count + new_tokens.count <= swap_array->max_count);
|
||||
memcpy(swap_array->tokens + swap_array->count, new_tokens.tokens, new_tokens.count*sizeof(Cpp_Token));
|
||||
swap_array->count += new_tokens.count;
|
||||
Assert(new_array.count + new_tokens.count <= new_array.max_count);
|
||||
memcpy(new_array.tokens + new_array.count, new_tokens.tokens, new_tokens.count*sizeof(Cpp_Token));
|
||||
new_array.count += new_tokens.count;
|
||||
new_tokens.count = 0;
|
||||
|
||||
if (result == LexResult_Finished){
|
||||
|
@ -246,155 +98,23 @@ file_first_lex_serial(System_Functions *system, Models *models, Editing_File *fi
|
|||
} while (still_lexing);
|
||||
|
||||
Cpp_Token_Array *token_array = &file->state.token_array;
|
||||
token_array->count = swap_array->count;
|
||||
token_array->max_count = swap_array->max_count;
|
||||
token_array->count = new_array.count;
|
||||
token_array->max_count = new_array.max_count;
|
||||
if (token_array->tokens != 0){
|
||||
heap_free(heap, token_array->tokens);
|
||||
}
|
||||
token_array->tokens = swap_array->tokens;
|
||||
token_array->tokens = new_array.tokens;
|
||||
|
||||
swap_array->tokens = 0;
|
||||
swap_array->count = 0;
|
||||
swap_array->max_count = 0;
|
||||
|
||||
file->state.tokens_complete = true;
|
||||
file->state.still_lexing = false;
|
||||
new_array.tokens = 0;
|
||||
new_array.count = 0;
|
||||
new_array.max_count = 0;
|
||||
|
||||
end_temp(temp);
|
||||
|
||||
file->state.tokens_complete = true;
|
||||
file_mark_edit_finished(&models->working_set, file);
|
||||
}
|
||||
}
|
||||
|
||||
internal b32
|
||||
file_relex_parallel(System_Functions *system, Models *models, Editing_File *file, i32 start_i, i32 end_i, i32 shift_amount){
|
||||
Mem_Options *mem = &models->mem;
|
||||
Arena *scratch = &mem->arena;
|
||||
Heap *heap = &mem->heap;
|
||||
|
||||
b32 result = true;
|
||||
if (file->state.token_array.tokens == 0){
|
||||
file_first_lex_parallel(system, models, file);
|
||||
}
|
||||
else{
|
||||
b32 internal_lex = !file->state.still_lexing;
|
||||
if (internal_lex){
|
||||
Gap_Buffer *buffer = &file->state.buffer;
|
||||
i32 extra_tolerance = 100;
|
||||
|
||||
Cpp_Token_Array *array = &file->state.token_array;
|
||||
Cpp_Relex_Range relex_range = cpp_get_relex_range(array, start_i, end_i);
|
||||
|
||||
i32 relex_space_size = relex_range.end_token_index - relex_range.start_token_index + extra_tolerance;
|
||||
|
||||
Temp_Memory temp = begin_temp(scratch);
|
||||
Parse_Context parse_context = parse_context_get(scratch, &models->parse_context_memory, file->settings.parse_context_id);
|
||||
Assert(parse_context.valid);
|
||||
|
||||
Cpp_Token_Array relex_array;
|
||||
relex_array.count = 0;
|
||||
relex_array.max_count = relex_space_size;
|
||||
relex_array.tokens = push_array(scratch, Cpp_Token, relex_array.max_count);
|
||||
|
||||
i32 size = buffer_size(buffer);
|
||||
|
||||
Cpp_Relex_Data state = cpp_relex_init(array, start_i, end_i, shift_amount, file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table);
|
||||
|
||||
String_Const_u8_Array chunk_space[3];
|
||||
Cursor chunk_cursor = make_cursor(chunk_space, sizeof(chunk_space));
|
||||
String_Const_u8_Array chunks = buffer_get_chunks(&chunk_cursor, buffer, BufferGetChunk_ZeroTerminated);
|
||||
|
||||
i32 chunk_index = 0;
|
||||
u8 *chunk = chunks.vals[chunk_index].str;
|
||||
umem chunk_size = chunks.vals[chunk_index].size;
|
||||
|
||||
for (;!cpp_relex_is_start_chunk(&state, (char*)chunk, (i32)chunk_size);){
|
||||
++chunk_index;
|
||||
Assert(chunk_index < chunks.count);
|
||||
chunk = chunks.vals[chunk_index].str;
|
||||
chunk_size = chunks.vals[chunk_index].size;
|
||||
}
|
||||
|
||||
for (;;){
|
||||
Cpp_Lex_Result lex_result =
|
||||
cpp_relex_step(&state, (char*)chunk, (i32)chunk_size, size, array, &relex_array);
|
||||
|
||||
switch (lex_result){
|
||||
case LexResult_NeedChunk:
|
||||
{
|
||||
++chunk_index;
|
||||
Assert(chunk_index < chunks.count);
|
||||
chunk = chunks.vals[chunk_index].str;
|
||||
chunk_size = chunks.vals[chunk_index].size;
|
||||
}break;
|
||||
|
||||
case LexResult_NeedTokenMemory:
|
||||
{
|
||||
internal_lex = false;
|
||||
}goto doublebreak;
|
||||
|
||||
case LexResult_Finished: goto doublebreak;
|
||||
}
|
||||
}
|
||||
doublebreak:;
|
||||
|
||||
if (internal_lex){
|
||||
i32 new_count = cpp_relex_get_new_count(&state, array->count, &relex_array);
|
||||
if (new_count > array->max_count){
|
||||
i32 new_max = round_up_i32(new_count, KB(1));
|
||||
void *memory = heap_allocate(heap, new_max*sizeof(Cpp_Token));
|
||||
memcpy(memory, array->tokens, array->count*sizeof(Cpp_Token));
|
||||
heap_free(heap, array->tokens);
|
||||
array->tokens = (Cpp_Token*)memory;
|
||||
array->max_count = new_max;
|
||||
}
|
||||
|
||||
cpp_relex_complete(&state, array, &relex_array);
|
||||
file_mark_edit_finished(&models->working_set, file);
|
||||
}
|
||||
else{
|
||||
cpp_relex_abort(&state, array);
|
||||
}
|
||||
|
||||
end_temp(temp);
|
||||
}
|
||||
|
||||
if (!internal_lex){
|
||||
Cpp_Token_Array *array = &file->state.token_array;
|
||||
Cpp_Get_Token_Result get_token_result = cpp_get_token(*array, end_i);
|
||||
i32 end_token_i = get_token_result.token_index;
|
||||
|
||||
if (end_token_i < 0){
|
||||
end_token_i = 0;
|
||||
}
|
||||
else if (end_i > array->tokens[end_token_i].start){
|
||||
++end_token_i;
|
||||
}
|
||||
|
||||
cpp_shift_token_starts(array, end_token_i, shift_amount);
|
||||
--end_token_i;
|
||||
if (end_token_i >= 0){
|
||||
Cpp_Token *token = array->tokens + end_token_i;
|
||||
if (token->start < end_i && token->start + token->size > end_i){
|
||||
token->size += shift_amount;
|
||||
}
|
||||
}
|
||||
|
||||
file->state.still_lexing = true;
|
||||
|
||||
Job_Data job = {};
|
||||
job.callback = job_full_lex;
|
||||
job.data[0] = file;
|
||||
job.data[1] = heap;
|
||||
job.data[2] = models;
|
||||
file->state.lex_job = system->post_job(BACKGROUND_THREADS, job);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal b32
|
||||
file_relex_serial(System_Functions *system, Models *models, Editing_File *file, i32 start_i, i32 end_i, i32 shift_amount){
|
||||
Mem_Options *mem = &models->mem;
|
||||
|
@ -405,8 +125,6 @@ file_relex_serial(System_Functions *system, Models *models, Editing_File *file,
|
|||
file_first_lex_serial(system, models, file);
|
||||
}
|
||||
else{
|
||||
Assert(!file->state.still_lexing);
|
||||
|
||||
Gap_Buffer *buffer = &file->state.buffer;
|
||||
Cpp_Token_Array *array = &file->state.token_array;
|
||||
|
||||
|
@ -477,22 +195,12 @@ file_relex_serial(System_Functions *system, Models *models, Editing_File *file,
|
|||
|
||||
internal void
|
||||
file_first_lex(System_Functions *system, Models *models, Editing_File *file){
|
||||
if (!file->settings.virtual_white){
|
||||
file_first_lex_parallel(system, models, file);
|
||||
}
|
||||
else{
|
||||
file_first_lex_serial(system, models, file);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
file_relex(System_Functions *system, Models *models, Editing_File *file, i32 start, i32 end, i32 shift_amount){
|
||||
if (!file->settings.virtual_white){
|
||||
file_relex_parallel(system, models, file, start, end, shift_amount);
|
||||
}
|
||||
else{
|
||||
file_relex_serial(system, models, file, start, end, shift_amount);
|
||||
}
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 20.07.2016
|
||||
*
|
||||
* File tracking API.
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#if !defined(FILE_TRACK_4TECH_H)
|
||||
#define FILE_TRACK_4TECH_H
|
||||
|
||||
#if !defined(FILE_TRACK_LINK)
|
||||
# define FILE_TRACK_LINK static
|
||||
#endif
|
||||
|
||||
struct File_Track_System{
|
||||
u8 opaque[128];
|
||||
};
|
||||
|
||||
typedef i32 File_Track_Result;
|
||||
enum{
|
||||
FileTrack_Good = 0,
|
||||
FileTrack_MemoryTooSmall = 1,
|
||||
FileTrack_OutOfTableMemory = 2,
|
||||
FileTrack_OutOfListenerMemory = 3,
|
||||
FileTrack_NoMoreEvents = 4,
|
||||
FileTrack_FileSystemError = 5
|
||||
};
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
init_track_system(File_Track_System *system, Partition *scratch, void *table_memory, i32 table_memory_size, void *listener_memory, i32 listener_memory_size);
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
add_listener(File_Track_System *system, Partition *scratch, u8 *filename);
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
remove_listener(File_Track_System *system, Partition *scratch, u8 *filename);
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
move_track_system(File_Track_System *system, Partition *scratch, void *mem, i32 size);
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
expand_track_system_listeners(File_Track_System *system, Partition *scratch, void *mem, i32 size);
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
get_change_event(File_Track_System *system, Partition *scratch, u8 *buffer, i32 max, i32 *size);
|
||||
|
||||
FILE_TRACK_LINK File_Track_Result
|
||||
shut_down_track_system(File_Track_System *system, Partition *scratch);
|
||||
|
||||
#endif
|
||||
|
||||
// BOTTOM
|
149
4ed_system.h
149
4ed_system.h
|
@ -45,16 +45,6 @@ typedef Sys_Load_Close_Sig(System_Load_Close);
|
|||
#define Sys_Save_File_Sig(name) File_Attributes name(char *filename, char *buffer, u32 size)
|
||||
typedef Sys_Save_File_Sig(System_Save_File);
|
||||
|
||||
// file changes
|
||||
#define Sys_Add_Listener_Sig(name) b32 name(char *filename)
|
||||
typedef Sys_Add_Listener_Sig(System_Add_Listener);
|
||||
|
||||
#define Sys_Remove_Listener_Sig(name) b32 name(char *filename)
|
||||
typedef Sys_Remove_Listener_Sig(System_Remove_Listener);
|
||||
|
||||
#define Sys_Get_File_Change_Sig(name) b32 name(char *buffer, i32 max, b32 *mem_too_small, i32 *required_size)
|
||||
typedef Sys_Get_File_Change_Sig(System_Get_File_Change);
|
||||
|
||||
// time
|
||||
#define Sys_Now_Time_Sig(name) u64 name()
|
||||
typedef Sys_Now_Time_Sig(System_Now_Time);
|
||||
|
@ -98,98 +88,49 @@ typedef Sys_CLI_Update_Step_Sig(System_CLI_Update_Step);
|
|||
#define Sys_CLI_End_Update_Sig(name) b32 name(CLI_Handles *cli)
|
||||
typedef Sys_CLI_End_Update_Sig(System_CLI_End_Update);
|
||||
|
||||
// coroutine
|
||||
|
||||
struct Coroutine_Head{
|
||||
void *in;
|
||||
void *out;
|
||||
};
|
||||
|
||||
#define COROUTINE_SIG(n) void n(Coroutine_Head *coroutine)
|
||||
typedef COROUTINE_SIG(Coroutine_Function);
|
||||
|
||||
#define Sys_Create_Coroutine_Sig(name) Coroutine_Head *name(Coroutine_Function *func)
|
||||
typedef Sys_Create_Coroutine_Sig(System_Create_Coroutine);
|
||||
|
||||
#define Sys_Launch_Coroutine_Sig(name) Coroutine_Head *name(Coroutine_Head *head, void *in, void *out)
|
||||
typedef Sys_Launch_Coroutine_Sig(System_Launch_Coroutine);
|
||||
|
||||
#define Sys_Resume_Coroutine_Sig(name) Coroutine_Head *name(Coroutine_Head *head, void *in, void *out)
|
||||
typedef Sys_Resume_Coroutine_Sig(System_Resume_Coroutine);
|
||||
|
||||
#define Sys_Yield_Coroutine_Sig(name) void name(Coroutine_Head *head)
|
||||
typedef Sys_Yield_Coroutine_Sig(System_Yield_Coroutine);
|
||||
|
||||
//
|
||||
|
||||
#define Sys_Open_Color_Picker_Sig(name) void name(Color_Picker *picker)
|
||||
typedef Sys_Open_Color_Picker_Sig(System_Open_Color_Picker);
|
||||
|
||||
// thread
|
||||
struct Thread_Context;
|
||||
typedef Plat_Handle System_Thread;
|
||||
typedef Plat_Handle System_Mutex;
|
||||
typedef Plat_Handle System_Condition_Variable;
|
||||
typedef void Thread_Function(void *ptr);
|
||||
|
||||
enum Lock_ID{
|
||||
FRAME_LOCK,
|
||||
CANCEL_LOCK0,
|
||||
CANCEL_LOCK1,
|
||||
CANCEL_LOCK2,
|
||||
CANCEL_LOCK3,
|
||||
CANCEL_LOCK4,
|
||||
CANCEL_LOCK5,
|
||||
CANCEL_LOCK6,
|
||||
CANCEL_LOCK7,
|
||||
LOCK_COUNT
|
||||
};
|
||||
#define Sys_Thread_Launch_Sig(name) System_Thread name(Thread_Function *proc, void *ptr)
|
||||
typedef Sys_Thread_Launch_Sig(System_Thread_Launch);
|
||||
|
||||
enum Thread_Group_ID{
|
||||
BACKGROUND_THREADS,
|
||||
THREAD_GROUP_COUNT
|
||||
};
|
||||
#define Sys_Thread_Join_Sig(name) void name(System_Thread thread)
|
||||
typedef Sys_Thread_Join_Sig(System_Thread_Join);
|
||||
|
||||
struct Thread_Memory{
|
||||
void *data;
|
||||
u32 size;
|
||||
u32 id;
|
||||
};
|
||||
global Thread_Memory null_thread_memory = {};
|
||||
#define Sys_Thread_Free_Sig(name) void name(System_Thread thread)
|
||||
typedef Sys_Thread_Free_Sig(System_Thread_Free);
|
||||
|
||||
struct Thread_Exchange;
|
||||
struct System_Functions;
|
||||
#define Sys_Mutex_Make_Sig(name) System_Mutex name(void)
|
||||
typedef Sys_Mutex_Make_Sig(System_Mutex_Make);
|
||||
|
||||
#define Job_Callback_Sig(name) void name( \
|
||||
System_Functions *system, \
|
||||
Thread_Context *thread, \
|
||||
Thread_Memory *memory, \
|
||||
void *data[4])
|
||||
#define Sys_Mutex_Acquire_Sig(name) void name(System_Mutex mutex)
|
||||
typedef Sys_Mutex_Acquire_Sig(System_Mutex_Acquire);
|
||||
|
||||
typedef void Job_Callback(System_Functions *system, Thread_Context *thread, Thread_Memory *memory, void *data[4]);
|
||||
#define Sys_Mutex_Release_Sig(name) void name(System_Mutex mutex)
|
||||
typedef Sys_Mutex_Release_Sig(System_Mutex_Release);
|
||||
|
||||
struct Job_Data{
|
||||
Job_Callback *callback;
|
||||
void *data[4];
|
||||
};
|
||||
#define Sys_Mutex_Free_Sig(name) void name(System_Mutex mutex)
|
||||
typedef Sys_Mutex_Free_Sig(System_Mutex_Free);
|
||||
|
||||
#define QUEUE_WRAP 256
|
||||
#define Sys_Condition_Variable_Make_Sig(name) System_Condition_Variable name(void)
|
||||
typedef Sys_Condition_Variable_Make_Sig(System_Condition_Variable_Make);
|
||||
|
||||
#define THREAD_NOT_ASSIGNED 0xFFFFFFFF
|
||||
#define Sys_Condition_Variable_Wait_Sig(name) void name(System_Condition_Variable cv, System_Mutex mutex)
|
||||
typedef Sys_Condition_Variable_Wait_Sig(System_Condition_Variable_Wait);
|
||||
|
||||
#define Sys_Post_Job_Sig(name) u32 name(Thread_Group_ID group_id, Job_Data job)
|
||||
typedef Sys_Post_Job_Sig(System_Post_Job);
|
||||
#define Sys_Condition_Variable_Signal_Sig(name) void name(System_Condition_Variable cv)
|
||||
typedef Sys_Condition_Variable_Signal_Sig(System_Condition_Variable_Signal);
|
||||
|
||||
#define Sys_Cancel_Job_Sig(name) void name(Thread_Group_ID group_id, u32 job_id)
|
||||
typedef Sys_Cancel_Job_Sig(System_Cancel_Job);
|
||||
|
||||
#define Sys_Check_Cancel_Sig(name) b32 name(Thread_Context *thread)
|
||||
typedef Sys_Check_Cancel_Sig(System_Check_Cancel);
|
||||
|
||||
#define Sys_Grow_Thread_Memory_Sig(name) void name(Thread_Memory *memory)
|
||||
typedef Sys_Grow_Thread_Memory_Sig(System_Grow_Thread_Memory);
|
||||
|
||||
#define Sys_Acquire_Lock_Sig(name) void name(i32 id)
|
||||
typedef Sys_Acquire_Lock_Sig(System_Acquire_Lock);
|
||||
|
||||
#define Sys_Release_Lock_Sig(name) void name(i32 id)
|
||||
typedef Sys_Release_Lock_Sig(System_Release_Lock);
|
||||
#define Sys_Condition_Variable_Free_Sig(name) void name(System_Condition_Variable cv)
|
||||
typedef Sys_Condition_Variable_Free_Sig(System_Condition_Variable_Free);
|
||||
|
||||
// memory
|
||||
#define Sys_Memory_Allocate_Sig(name) void* name(umem size)
|
||||
|
@ -223,12 +164,9 @@ struct System_Functions{
|
|||
Graphics_Get_Texture_Function *get_texture;
|
||||
Graphics_Fill_Texture_Function *fill_texture;
|
||||
|
||||
// files (tracked api): 11
|
||||
// files
|
||||
System_Get_Canonical *get_canonical;
|
||||
System_Get_File_List *get_file_list;
|
||||
System_Add_Listener *add_listener;
|
||||
System_Remove_Listener *remove_listener;
|
||||
System_Get_File_Change *get_file_change;
|
||||
System_Quick_File_Attributes *quick_file_attributes;
|
||||
System_Load_Handle *load_handle;
|
||||
System_Load_Attributes *load_attributes;
|
||||
|
@ -236,22 +174,16 @@ struct System_Functions{
|
|||
System_Load_Close *load_close;
|
||||
System_Save_File *save_file;
|
||||
|
||||
// time: 4
|
||||
// time
|
||||
System_Now_Time *now_time;
|
||||
System_Wake_Up_Timer_Create *wake_up_timer_create;
|
||||
System_Wake_Up_Timer_Release *wake_up_timer_release;
|
||||
System_Wake_Up_Timer_Set *wake_up_timer_set;
|
||||
|
||||
// clipboard: 1
|
||||
// clipboard
|
||||
System_Post_Clipboard *post_clipboard;
|
||||
|
||||
// coroutine: 4
|
||||
System_Create_Coroutine *create_coroutine;
|
||||
System_Launch_Coroutine *launch_coroutine;
|
||||
System_Resume_Coroutine *resume_coroutine;
|
||||
System_Yield_Coroutine *yield_coroutine;
|
||||
|
||||
// cli: 4
|
||||
// cli
|
||||
System_CLI_Call *cli_call;
|
||||
System_CLI_Begin_Update *cli_begin_update;
|
||||
System_CLI_Update_Step *cli_update_step;
|
||||
|
@ -260,15 +192,20 @@ struct System_Functions{
|
|||
// TODO(allen):
|
||||
System_Open_Color_Picker *open_color_picker;
|
||||
|
||||
// threads: 6
|
||||
System_Post_Job *post_job;
|
||||
System_Cancel_Job *cancel_job;
|
||||
System_Check_Cancel *check_cancel;
|
||||
System_Grow_Thread_Memory *grow_thread_memory;
|
||||
System_Acquire_Lock *acquire_lock;
|
||||
System_Release_Lock *release_lock;
|
||||
// threads
|
||||
System_Thread_Launch *thread_launch;
|
||||
System_Thread_Join *thread_join;
|
||||
System_Thread_Free *thread_free;
|
||||
System_Mutex_Make *mutex_make;
|
||||
System_Mutex_Acquire *mutex_acquire;
|
||||
System_Mutex_Release *mutex_release;
|
||||
System_Mutex_Free *mutex_free;
|
||||
System_Condition_Variable_Make *condition_variable_make;
|
||||
System_Condition_Variable_Wait *condition_variable_wait;
|
||||
System_Condition_Variable_Signal *condition_variable_signal;
|
||||
System_Condition_Variable_Free *condition_variable_free;
|
||||
|
||||
// custom: 9
|
||||
// custom
|
||||
System_Memory_Allocate *memory_allocate;
|
||||
System_Memory_Set_Protection *memory_set_protection;
|
||||
System_Memory_Free *memory_free;
|
||||
|
|
|
@ -524,9 +524,9 @@ render_loaded_file_in_view__inner(Models *models, Render_Target *target, View *v
|
|||
Assert(!file->is_dummy);
|
||||
Assert(buffer_good(&file->state.buffer));
|
||||
|
||||
b32 tokens_use = file->state.tokens_complete && (file->state.token_array.count > 0);
|
||||
Cpp_Token_Array token_array = file->state.token_array;
|
||||
b32 wrapped = !file->settings.unwrapped_lines;
|
||||
b32 tokens_use = (token_array.tokens != 0);
|
||||
b32 wrapped = (!file->settings.unwrapped_lines);
|
||||
Face_ID font_id = file->settings.font_id;
|
||||
|
||||
i32 *line_starts = file->state.buffer.line_starts;
|
||||
|
|
|
@ -307,7 +307,6 @@ file_bind_file_name(System_Functions *system, Heap *heap, Working_Set *working_s
|
|||
file->canon.name_size = size;
|
||||
block_copy(file->canon.name_space, canon_file_name.str, size);
|
||||
file_name_terminate(&file->canon);
|
||||
system->add_listener((char*)file->canon.name_space);
|
||||
b32 result = working_set_canon_add(working_set, file, string_from_file_name(&file->canon));
|
||||
Assert(result);
|
||||
}
|
||||
|
@ -316,7 +315,6 @@ internal void
|
|||
buffer_unbind_file(System_Functions *system, Working_Set *working_set, Editing_File *file){
|
||||
Assert(file->unique_name.name_size == 0);
|
||||
Assert(file->canon.name_size != 0);
|
||||
system->remove_listener((char*)file->canon.name_space);
|
||||
working_set_canon_remove(working_set, string_from_file_name(&file->canon));
|
||||
file->canon.name_size = 0;
|
||||
}
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 19.07.2017
|
||||
*
|
||||
* Coroutine implementation from thread+mutex+cv
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
typedef u32 Coroutine_State;
|
||||
enum{
|
||||
CoroutineState_Dead,
|
||||
CoroutineState_Active,
|
||||
CoroutineState_Inactive,
|
||||
CoroutineState_Waiting,
|
||||
};
|
||||
|
||||
typedef u32 Coroutine_Type;
|
||||
enum{
|
||||
CoroutineType_Uninitialized,
|
||||
CoroutineType_Root,
|
||||
CoroutineType_Sub,
|
||||
};
|
||||
|
||||
struct Coroutine{
|
||||
Coroutine_Head head;
|
||||
|
||||
Thread thread;
|
||||
Condition_Variable cv;
|
||||
struct Coroutine_System *sys;
|
||||
Coroutine_Function *function;
|
||||
Coroutine *yield_ctx;
|
||||
Coroutine_State state;
|
||||
Coroutine_Type type;
|
||||
};
|
||||
|
||||
struct Coroutine_System{
|
||||
Mutex lock;
|
||||
Condition_Variable init_cv;
|
||||
b32 did_init;
|
||||
Coroutine *active;
|
||||
};
|
||||
|
||||
#define COROUTINE_INT_SKIP_SLEEP false
|
||||
|
||||
internal void
|
||||
coroutine_internal_pass_control(Coroutine *me, Coroutine *other, Coroutine_State my_new_state, b32 do_sleep_loop = true){
|
||||
Assert(me->state == CoroutineState_Active);
|
||||
Assert(me->sys == other->sys);
|
||||
|
||||
me->state = my_new_state;
|
||||
other->state = CoroutineState_Active;
|
||||
me->sys->active = other;
|
||||
system_signal_cv(&other->cv, &me->sys->lock);
|
||||
if (do_sleep_loop){
|
||||
for (;me->state != CoroutineState_Active;){
|
||||
system_wait_cv(&me->cv, &me->sys->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal
|
||||
PLAT_THREAD_SIG(coroutine_main){
|
||||
Coroutine *me = (Coroutine*)ptr;
|
||||
|
||||
// NOTE(allen): Init handshake
|
||||
Assert(me->state == CoroutineState_Dead);
|
||||
system_acquire_lock(&me->sys->lock);
|
||||
me->sys->did_init = true;
|
||||
system_signal_cv(&me->sys->init_cv, &me->sys->lock);
|
||||
|
||||
for (;;){
|
||||
// NOTE(allen): Wait until someone wakes us up, then go into our procedure.
|
||||
for (;me->state != CoroutineState_Active;){
|
||||
system_wait_cv(&me->cv, &me->sys->lock);
|
||||
}
|
||||
Assert(me->type != CoroutineType_Root);
|
||||
Assert(me->yield_ctx != 0);
|
||||
Assert(me->function != 0);
|
||||
|
||||
me->function(&me->head);
|
||||
|
||||
// NOTE(allen): Wake up the caller and set this coroutine back to being dead.
|
||||
Coroutine *other = me->yield_ctx;
|
||||
Assert(other != 0);
|
||||
Assert(other->state == CoroutineState_Waiting);
|
||||
|
||||
coroutine_internal_pass_control(me, other, CoroutineState_Dead, COROUTINE_INT_SKIP_SLEEP);
|
||||
me->function = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
init_coroutine_system(Coroutine *root, Coroutine_System *sys){
|
||||
system_init_lock(&sys->lock);
|
||||
system_init_cv(&sys->init_cv);
|
||||
sys->active = root;
|
||||
|
||||
memset(root, 0, sizeof(*root));
|
||||
|
||||
root->sys = sys;
|
||||
root->state = CoroutineState_Active;
|
||||
root->type = CoroutineType_Root;
|
||||
|
||||
system_init_cv(&root->cv);
|
||||
|
||||
system_acquire_lock(&sys->lock);
|
||||
}
|
||||
|
||||
internal void
|
||||
init_coroutine_sub(Coroutine *co, Coroutine_System *sys){
|
||||
memset(co, 0, sizeof(*co));
|
||||
|
||||
co->sys = sys;
|
||||
co->state = CoroutineState_Dead;
|
||||
co->type = CoroutineType_Sub;
|
||||
|
||||
system_init_cv(&co->cv);
|
||||
|
||||
sys->did_init = false;
|
||||
system_init_and_launch_thread(&co->thread, coroutine_main, co);
|
||||
for (;!sys->did_init;){
|
||||
system_wait_cv(&sys->init_cv, &sys->lock);
|
||||
}
|
||||
}
|
||||
|
||||
// HACK(allen): I want to bundle this with launch!
|
||||
internal void
|
||||
coroutine_set_function(Coroutine *me, Coroutine_Function *function){
|
||||
Assert(me->state == CoroutineState_Dead);
|
||||
me->function = function;
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_launch(Coroutine *me, Coroutine *other){
|
||||
Assert(me->sys == other->sys);
|
||||
Assert(other->state == CoroutineState_Dead);
|
||||
Assert(other->function != 0);
|
||||
|
||||
other->yield_ctx = me;
|
||||
coroutine_internal_pass_control(me, other, CoroutineState_Waiting);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_yield(Coroutine *me){
|
||||
Coroutine *other = me->yield_ctx;
|
||||
Assert(other != 0);
|
||||
Assert(me->sys == other->sys);
|
||||
Assert(other->state == CoroutineState_Waiting);
|
||||
|
||||
coroutine_internal_pass_control(me, other, CoroutineState_Inactive);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_resume(Coroutine *me, Coroutine *other){
|
||||
Assert(me->sys == other->sys);
|
||||
Assert(other->state == CoroutineState_Inactive);
|
||||
|
||||
other->yield_ctx = me;
|
||||
coroutine_internal_pass_control(me, other, CoroutineState_Waiting);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
struct Coroutine_Alloc_Block{
|
||||
Coroutine coroutine;
|
||||
Coroutine_Alloc_Block *next;
|
||||
};
|
||||
|
||||
#define COROUTINE_SLOT_SIZE sizeof(Coroutine_Alloc_Block)
|
||||
|
||||
struct Coroutine_System_Auto_Alloc{
|
||||
Coroutine_System sys;
|
||||
|
||||
Coroutine root;
|
||||
Coroutine_Alloc_Block *head_free_uninit;
|
||||
Coroutine_Alloc_Block *head_free_inited;
|
||||
};
|
||||
|
||||
internal void
|
||||
init_coroutine_system(Coroutine_System_Auto_Alloc *sys){
|
||||
init_coroutine_system(&sys->root, &sys->sys);
|
||||
sys->head_free_uninit = 0;
|
||||
sys->head_free_inited = 0;
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_system_provide_memory(Coroutine_System_Auto_Alloc *sys, void *memory, umem size){
|
||||
memset(memory, 0, size);
|
||||
|
||||
Coroutine_Alloc_Block *blocks = (Coroutine_Alloc_Block*)memory;
|
||||
umem count = (size / sizeof(*blocks));
|
||||
|
||||
Coroutine_Alloc_Block *old_head = sys->head_free_uninit;
|
||||
sys->head_free_uninit = &blocks[0];
|
||||
Coroutine_Alloc_Block *block = blocks;
|
||||
for (u32 i = 1; i < count; ++i, ++block){
|
||||
block->next = block + 1;
|
||||
}
|
||||
block->next = old_head;
|
||||
}
|
||||
|
||||
internal Coroutine_Alloc_Block*
|
||||
coroutine_system_pop(Coroutine_Alloc_Block **head){
|
||||
Coroutine_Alloc_Block *result = *head;
|
||||
if (result != 0){
|
||||
*head = result->next;
|
||||
}
|
||||
result->next = 0;
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_system_push(Coroutine_Alloc_Block **head, Coroutine_Alloc_Block *block){
|
||||
block->next = *head;
|
||||
*head = block;
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_system_force_init(Coroutine_System_Auto_Alloc *sys, u32 count){
|
||||
for (u32 i = 0; i < count; ++i){
|
||||
Coroutine_Alloc_Block *block = coroutine_system_pop(&sys->head_free_uninit);
|
||||
if (block == 0){
|
||||
break;
|
||||
}
|
||||
init_coroutine_sub(&block->coroutine, &sys->sys);
|
||||
coroutine_system_push(&sys->head_free_inited, block);
|
||||
}
|
||||
}
|
||||
|
||||
internal Coroutine*
|
||||
coroutine_system_alloc(Coroutine_System_Auto_Alloc *sys){
|
||||
Coroutine_Alloc_Block *block = coroutine_system_pop(&sys->head_free_inited);
|
||||
if (block == 0){
|
||||
coroutine_system_force_init(sys, 1);
|
||||
block = coroutine_system_pop(&sys->head_free_inited);
|
||||
}
|
||||
|
||||
Coroutine *result = 0;
|
||||
if (block != 0){
|
||||
result = &block->coroutine;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutine_system_free(Coroutine_System_Auto_Alloc *sys, Coroutine *co){
|
||||
Coroutine_Alloc_Block *block = (Coroutine_Alloc_Block*)co;
|
||||
coroutine_system_push(&sys->head_free_inited, block);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 10.09.2017
|
||||
*
|
||||
* Mac C++ layer for 4coder
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#if !defined(FRED_COROUTINE_FUNCTIONS_CPP)
|
||||
#define FRED_COROUTINE_FUNCTIONS_CPP
|
||||
|
||||
//
|
||||
// Coroutine
|
||||
//
|
||||
|
||||
internal
|
||||
Sys_Create_Coroutine_Sig(system_create_coroutine){
|
||||
Coroutine *coroutine = coroutine_system_alloc(&coroutines);
|
||||
Coroutine_Head *result = 0;
|
||||
if (coroutine != 0){
|
||||
coroutine_set_function(coroutine, func);
|
||||
result = &coroutine->head;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Launch_Coroutine_Sig(system_launch_coroutine){
|
||||
Coroutine *coroutine = (Coroutine*)head;
|
||||
coroutine->head.in = in;
|
||||
coroutine->head.out = out;
|
||||
|
||||
Coroutine *active = coroutine->sys->active;
|
||||
Assert(active != 0);
|
||||
coroutine_launch(active, coroutine);
|
||||
Assert(active == coroutine->sys->active);
|
||||
|
||||
Coroutine_Head *result = &coroutine->head;
|
||||
if (coroutine->state == CoroutineState_Dead){
|
||||
coroutine_system_free(&coroutines, coroutine);
|
||||
result = 0;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
Sys_Resume_Coroutine_Sig(system_resume_coroutine){
|
||||
Coroutine *coroutine = (Coroutine*)head;
|
||||
coroutine->head.in = in;
|
||||
coroutine->head.out = out;
|
||||
|
||||
Coroutine *active = coroutine->sys->active;
|
||||
Assert(active != 0);
|
||||
coroutine_resume(active, coroutine);
|
||||
Assert(active == coroutine->sys->active);
|
||||
|
||||
Coroutine_Head *result = &coroutine->head;
|
||||
if (coroutine->state == CoroutineState_Dead){
|
||||
coroutine_system_free(&coroutines, coroutine);
|
||||
result = 0;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
Sys_Yield_Coroutine_Sig(system_yield_coroutine){
|
||||
Coroutine *coroutine = (Coroutine*)head;
|
||||
coroutine_yield(coroutine);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// BOTTOM
|
||||
|
||||
|
|
@ -14,12 +14,9 @@
|
|||
#define SYSLINK(name) sysfunc.name = system_##name
|
||||
|
||||
internal void
|
||||
link_system_code(){
|
||||
link_system_code(void){
|
||||
SYSLINK(get_canonical);
|
||||
SYSLINK(get_file_list);
|
||||
SYSLINK(add_listener);
|
||||
SYSLINK(remove_listener);
|
||||
SYSLINK(get_file_change);
|
||||
SYSLINK(quick_file_attributes);
|
||||
SYSLINK(load_handle);
|
||||
SYSLINK(load_attributes);
|
||||
|
@ -34,11 +31,6 @@ link_system_code(){
|
|||
|
||||
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);
|
||||
|
@ -46,12 +38,17 @@ link_system_code(){
|
|||
|
||||
SYSLINK(open_color_picker);
|
||||
|
||||
SYSLINK(post_job);
|
||||
SYSLINK(cancel_job);
|
||||
SYSLINK(check_cancel);
|
||||
SYSLINK(grow_thread_memory);
|
||||
SYSLINK(acquire_lock);
|
||||
SYSLINK(release_lock);
|
||||
SYSLINK(thread_launch);
|
||||
SYSLINK(thread_join);
|
||||
SYSLINK(thread_free);
|
||||
SYSLINK(mutex_make);
|
||||
SYSLINK(mutex_acquire);
|
||||
SYSLINK(mutex_release);
|
||||
SYSLINK(mutex_free);
|
||||
SYSLINK(condition_variable_make);
|
||||
SYSLINK(condition_variable_wait);
|
||||
SYSLINK(condition_variable_signal);
|
||||
SYSLINK(condition_variable_free);
|
||||
|
||||
SYSLINK(memory_allocate);
|
||||
SYSLINK(memory_set_protection);
|
||||
|
|
|
@ -136,15 +136,5 @@ read_command_line(Arena *scratch, i32 argc, char **argv){
|
|||
end_temp(temp);
|
||||
}
|
||||
|
||||
internal void
|
||||
coroutines_init(){
|
||||
init_coroutine_system(&coroutines);
|
||||
|
||||
umem size = COROUTINE_SLOT_SIZE*18;
|
||||
void *mem = system_memory_allocate(size);
|
||||
coroutine_system_provide_memory(&coroutines, mem, size);
|
||||
coroutine_system_force_init(&coroutines, 4);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 18.07.2017
|
||||
*
|
||||
* Cross platform threading constants
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#if !defined(FRED_SHARED_THREADING_CONSTANTS_H)
|
||||
#define FRED_SHARED_THREADING_CONSTANTS_H
|
||||
|
||||
#define CORE_COUNT 8
|
||||
|
||||
#define THREAD_TYPE_SIZE 32
|
||||
#define MUTEX_TYPE_SIZE 64
|
||||
#define CONDITION_VARIABLE_TYPE_SIZE 48
|
||||
#define SEMAPHORE_TYPE_SIZE 32
|
||||
|
||||
/*
|
||||
fprintf(stdout, "%d VS %d\n", (i32)sizeof(Thread), THREAD_TYPE_SIZE); \
|
||||
fprintf(stdout, "%d VS %d\n", (i32)sizeof(Mutex), MUTEX_TYPE_SIZE); \
|
||||
fprintf(stdout, "%d VS %d\n", (i32)sizeof(Condition_Variable), CONDITION_VARIABLE_TYPE_SIZE); \
|
||||
fprintf(stdout, "%d VS %d\n", (i32)sizeof(Semaphore), SEMAPHORE_TYPE_SIZE); \
|
||||
*/
|
||||
|
||||
#define AssertThreadSizes() \
|
||||
Assert( sizeof(Thread) == THREAD_TYPE_SIZE ); \
|
||||
Assert( sizeof(Mutex) == MUTEX_TYPE_SIZE ); \
|
||||
Assert(sizeof(Condition_Variable) == CONDITION_VARIABLE_TYPE_SIZE); \
|
||||
Assert( sizeof(Semaphore) == SEMAPHORE_TYPE_SIZE )
|
||||
|
||||
#endif
|
||||
|
||||
// BOTTOM
|
||||
|
|
@ -1,398 +0,0 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 18.07.2017
|
||||
*
|
||||
* Cross platform logic for work queues.
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
enum CV_ID{
|
||||
CANCEL_CV0,
|
||||
CANCEL_CV1,
|
||||
CANCEL_CV2,
|
||||
CANCEL_CV3,
|
||||
CANCEL_CV4,
|
||||
CANCEL_CV5,
|
||||
CANCEL_CV6,
|
||||
CANCEL_CV7,
|
||||
CV_COUNT
|
||||
};
|
||||
|
||||
struct Full_Job_Data{
|
||||
Job_Data job;
|
||||
u32 running_thread;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct Unbounded_Work_Queue{
|
||||
Full_Job_Data *jobs;
|
||||
i32 count, max, skip;
|
||||
u32 next_job_id;
|
||||
};
|
||||
|
||||
struct Work_Queue{
|
||||
Full_Job_Data jobs[QUEUE_WRAP];
|
||||
Semaphore semaphore;
|
||||
volatile u32 write_position;
|
||||
volatile u32 read_position;
|
||||
};
|
||||
|
||||
struct Thread_Context{
|
||||
u32 job_id;
|
||||
b32 running;
|
||||
b32 cancel;
|
||||
|
||||
Work_Queue *queue;
|
||||
u32 id;
|
||||
u32 group_id;
|
||||
u32 windows_id;
|
||||
Thread thread;
|
||||
};
|
||||
|
||||
struct Thread_Group{
|
||||
Thread_Context *threads;
|
||||
i32 count;
|
||||
|
||||
Unbounded_Work_Queue queue;
|
||||
|
||||
i32 cancel_lock0;
|
||||
i32 cancel_cv0;
|
||||
};
|
||||
|
||||
struct Threading_Vars{
|
||||
Thread_Memory *thread_memory;
|
||||
|
||||
Work_Queue queues[THREAD_GROUP_COUNT];
|
||||
Thread_Group groups[THREAD_GROUP_COUNT];
|
||||
Mutex locks[LOCK_COUNT];
|
||||
Condition_Variable conds[CV_COUNT];
|
||||
};
|
||||
global Threading_Vars threadvars;
|
||||
|
||||
internal
|
||||
Sys_Acquire_Lock_Sig(system_acquire_lock){
|
||||
system_acquire_lock(&threadvars.locks[id]);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Release_Lock_Sig(system_release_lock){
|
||||
system_release_lock(&threadvars.locks[id]);
|
||||
}
|
||||
|
||||
internal
|
||||
PLAT_THREAD_SIG(job_thread_proc){
|
||||
Thread_Context *thread = (Thread_Context*)ptr;
|
||||
|
||||
Work_Queue *queue = threadvars.queues + thread->group_id;
|
||||
Thread_Group *group = threadvars.groups + thread->group_id;
|
||||
|
||||
i32 thread_index = thread->id - 1;
|
||||
Thread_Memory *thread_memory = threadvars.thread_memory + thread_index;
|
||||
|
||||
i32 cancel_lock_id = group->cancel_lock0 + thread_index;
|
||||
i32 cancel_cv_id = group->cancel_cv0 + thread_index;
|
||||
Mutex *cancel_lock = &threadvars.locks[cancel_lock_id];
|
||||
Condition_Variable *cancel_cv = &threadvars.conds[cancel_cv_id];
|
||||
|
||||
if (thread_memory->size == 0){
|
||||
i32 new_size = KB(64);
|
||||
thread_memory->data = system_memory_allocate(new_size);
|
||||
thread_memory->size = new_size;
|
||||
}
|
||||
|
||||
for (;;){
|
||||
u32 read_index = queue->read_position;
|
||||
u32 write_index = queue->write_position;
|
||||
|
||||
if (read_index != write_index){
|
||||
u32 next_read_index = (read_index + 1) % QUEUE_WRAP;
|
||||
u32 safe_read_index = InterlockedCompareExchange(&queue->read_position, next_read_index, read_index);
|
||||
|
||||
if (safe_read_index == read_index){
|
||||
Full_Job_Data *full_job = queue->jobs + safe_read_index;
|
||||
// NOTE(allen): This is interlocked so that it plays nice
|
||||
// with the cancel job routine, which may try to cancel this job
|
||||
// at the same time that we try to run it
|
||||
|
||||
i32 safe_running_thread = InterlockedCompareExchange(&full_job->running_thread, thread->id, THREAD_NOT_ASSIGNED);
|
||||
|
||||
if (safe_running_thread == THREAD_NOT_ASSIGNED){
|
||||
thread->job_id = full_job->id;
|
||||
thread->running = true;
|
||||
full_job->job.callback(&sysfunc, thread, thread_memory, full_job->job.data);
|
||||
system_schedule_step();
|
||||
thread->running = false;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
if (thread->cancel){
|
||||
thread->cancel = 0;
|
||||
system_signal_cv(cancel_cv, cancel_lock);
|
||||
}
|
||||
system_release_lock(cancel_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
system_wait_on_semaphore(&queue->semaphore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
initialize_unbounded_queue(Unbounded_Work_Queue *source_queue){
|
||||
i32 max = 512;
|
||||
source_queue->jobs = (Full_Job_Data*)system_memory_allocate(max*sizeof(Full_Job_Data));
|
||||
source_queue->count = 0;
|
||||
source_queue->max = max;
|
||||
source_queue->skip = 0;
|
||||
}
|
||||
|
||||
internal i32
|
||||
get_work_queue_available_space(i32 write, i32 read){
|
||||
// NOTE(allen): The only time that queue->write_position == queue->read_position
|
||||
// is allowed is when the queue is empty. Thus if (write_position+1 == read_position)
|
||||
// the available space is zero. So these computations both end up leaving one slot unused.
|
||||
|
||||
// TODO(allen): The only way I can think to easily eliminate this is to have read and write wrap
|
||||
// at twice the size of the underlying array but modulo their values into the array then if write
|
||||
// has caught up with read it still will not be equal... but lots of modulos... ehh.
|
||||
|
||||
i32 available_space = 0;
|
||||
if (write >= read){
|
||||
available_space = QUEUE_WRAP - (write - read) - 1;
|
||||
}
|
||||
else{
|
||||
available_space = (read - write) - 1;
|
||||
}
|
||||
|
||||
return(available_space);
|
||||
}
|
||||
|
||||
#define UNBOUNDED_SKIP_MAX 128
|
||||
|
||||
internal i32
|
||||
flush_thread_group(Thread_Group_ID group_id){
|
||||
Thread_Group *group = threadvars.groups + group_id;
|
||||
Work_Queue *queue = threadvars.queues + group_id;
|
||||
Unbounded_Work_Queue *source_queue = &group->queue;
|
||||
i32 thread_count = group->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
|
||||
// main thread in this system, so it will not be interlocked.
|
||||
u32 read_position = queue->read_position;
|
||||
u32 write_position = queue->write_position;
|
||||
u32 available_space = get_work_queue_available_space(write_position, read_position);
|
||||
u32 available_jobs = source_queue->count - source_queue->skip;
|
||||
|
||||
u32 writable_count = Min(available_space, available_jobs);
|
||||
|
||||
if (writable_count > 0){
|
||||
u32 count1 = writable_count;
|
||||
|
||||
if (count1+write_position > QUEUE_WRAP){
|
||||
count1 = QUEUE_WRAP - write_position;
|
||||
}
|
||||
|
||||
u32 count2 = writable_count - count1;
|
||||
|
||||
Full_Job_Data *job_src1 = source_queue->jobs + source_queue->skip;
|
||||
Full_Job_Data *job_src2 = job_src1 + count1;
|
||||
|
||||
Full_Job_Data *job_dst1 = queue->jobs + write_position;
|
||||
Full_Job_Data *job_dst2 = queue->jobs;
|
||||
|
||||
Assert((job_src1->id % QUEUE_WRAP) == write_position);
|
||||
|
||||
memcpy(job_dst1, job_src1, sizeof(Full_Job_Data)*count1);
|
||||
memcpy(job_dst2, job_src2, sizeof(Full_Job_Data)*count2);
|
||||
queue->write_position = (write_position + writable_count) % QUEUE_WRAP;
|
||||
|
||||
source_queue->skip += writable_count;
|
||||
|
||||
if (source_queue->skip == source_queue->count){
|
||||
source_queue->skip = source_queue->count = 0;
|
||||
}
|
||||
else if (source_queue->skip > UNBOUNDED_SKIP_MAX){
|
||||
u32 left_over = source_queue->count - source_queue->skip;
|
||||
memmove(source_queue->jobs, source_queue->jobs + source_queue->skip,
|
||||
sizeof(Full_Job_Data)*left_over);
|
||||
source_queue->count = left_over;
|
||||
source_queue->skip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
i32 semaphore_release_count = writable_count;
|
||||
if (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);
|
||||
}
|
||||
|
||||
// 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 = threadvars.groups + group_id;
|
||||
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 = {};
|
||||
full_job.job = job;
|
||||
full_job.running_thread = THREAD_NOT_ASSIGNED;
|
||||
full_job.id = result;
|
||||
|
||||
queue->jobs[queue->count++] = full_job;
|
||||
flush_thread_group(group_id);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Cancel_Job_Sig(system_cancel_job){
|
||||
Thread_Group *group = threadvars.groups + group_id;
|
||||
Work_Queue *queue = threadvars.queues + 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){
|
||||
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_id = group->cancel_lock0 + thread_index;
|
||||
i32 cancel_cv_id = group->cancel_cv0 + thread_index;
|
||||
Mutex *cancel_lock = &threadvars.locks[cancel_lock_id];
|
||||
Condition_Variable *cancel_cv = &threadvars.conds[cancel_cv_id];
|
||||
|
||||
Thread_Context *thread = group->threads + thread_index;
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
thread->cancel = true;
|
||||
system_release_lock(&threadvars.locks[FRAME_LOCK]);
|
||||
|
||||
do{
|
||||
system_wait_cv(cancel_cv, cancel_lock);
|
||||
}while (thread->cancel);
|
||||
|
||||
system_acquire_lock(&threadvars.locks[FRAME_LOCK]);
|
||||
system_release_lock(cancel_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Check_Cancel_Sig(system_check_cancel){
|
||||
Thread_Group *group = threadvars.groups + thread->group_id;
|
||||
|
||||
b32 result = false;
|
||||
i32 thread_index = thread->id - 1;
|
||||
i32 cancel_lock_id = group->cancel_lock0 + thread_index;
|
||||
Mutex *cancel_lock = &threadvars.locks[cancel_lock_id];
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
if (thread->cancel){
|
||||
result = true;
|
||||
}
|
||||
system_release_lock(cancel_lock);
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal
|
||||
Sys_Grow_Thread_Memory_Sig(system_grow_thread_memory){
|
||||
Mutex *cancel_lock = &threadvars.locks[CANCEL_LOCK0 + memory->id - 1];
|
||||
|
||||
system_acquire_lock(cancel_lock);
|
||||
void *old_data = memory->data;
|
||||
i32 old_size = memory->size;
|
||||
i32 new_size = 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_lock);
|
||||
}
|
||||
|
||||
internal void
|
||||
work_system_init(){
|
||||
AssertThreadSizes();
|
||||
|
||||
u32 core_count = CORE_COUNT;
|
||||
Arena thread_arena = make_arena_system(&sysfunc);
|
||||
|
||||
for (i32 i = 0; i < LOCK_COUNT; ++i){
|
||||
system_init_lock(&threadvars.locks[i]);
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < CV_COUNT; ++i){
|
||||
system_init_cv(&threadvars.conds[i]);
|
||||
}
|
||||
|
||||
threadvars.thread_memory = push_array(&thread_arena, Thread_Memory, core_count);
|
||||
|
||||
for (u32 group_i = 0; group_i < THREAD_GROUP_COUNT; ++group_i){
|
||||
Thread_Context *threads = push_array(&thread_arena, Thread_Context, core_count);
|
||||
threadvars.groups[group_i].threads = threads;
|
||||
threadvars.groups[group_i].count = core_count;
|
||||
threadvars.groups[group_i].cancel_lock0 = CANCEL_LOCK0;
|
||||
threadvars.groups[group_i].cancel_cv0 = CANCEL_CV0;
|
||||
|
||||
system_init_semaphore(&threadvars.queues[group_i].semaphore, core_count);
|
||||
|
||||
for (u32 i = 0; i < core_count; ++i){
|
||||
Thread_Context *thread = threads + i;
|
||||
thread->id = i + 1;
|
||||
thread->group_id = group_i;
|
||||
|
||||
Thread_Memory *memory = &threadvars.thread_memory[i];
|
||||
memset(memory, 0, sizeof(*memory));
|
||||
memory->id = thread->id;
|
||||
|
||||
thread->queue = &threadvars.queues[group_i];
|
||||
system_init_and_launch_thread(&thread->thread, job_thread_proc, thread);
|
||||
}
|
||||
|
||||
initialize_unbounded_queue(&threadvars.groups[group_i].queue);
|
||||
}
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Mr. 4th Dimention - Allen Webster
|
||||
*
|
||||
* 18.07.2017
|
||||
*
|
||||
* Win32 threading wrapper
|
||||
*
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#if !defined(WIN32_THREADING_WRAPPER)
|
||||
#define WIN32_THREADING_WRAPPER
|
||||
|
||||
#define PLAT_THREAD_SIG(n) DWORD CALL_CONVENTION n(LPVOID ptr)
|
||||
typedef PLAT_THREAD_SIG(Thread_Function);
|
||||
|
||||
union Thread{
|
||||
HANDLE t;
|
||||
FixSize(THREAD_TYPE_SIZE);
|
||||
};
|
||||
|
||||
union Mutex{
|
||||
CRITICAL_SECTION crit;
|
||||
FixSize(MUTEX_TYPE_SIZE);
|
||||
};
|
||||
|
||||
union Condition_Variable{
|
||||
CONDITION_VARIABLE cv;
|
||||
FixSize(CONDITION_VARIABLE_TYPE_SIZE);
|
||||
};
|
||||
|
||||
union Semaphore{
|
||||
HANDLE s;
|
||||
FixSize(SEMAPHORE_TYPE_SIZE);
|
||||
};
|
||||
|
||||
|
||||
internal void
|
||||
system_init_and_launch_thread(Thread *t, Thread_Function *proc, void *ptr){
|
||||
t->t = CreateThread(0, 0, proc, ptr, 0, 0);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_init_lock(Mutex *m){
|
||||
InitializeCriticalSection(&m->crit);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_acquire_lock(Mutex *m){
|
||||
EnterCriticalSection(&m->crit);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_release_lock(Mutex *m){
|
||||
LeaveCriticalSection(&m->crit);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_init_cv(Condition_Variable *cv){
|
||||
InitializeConditionVariable(&cv->cv);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_wait_cv(Condition_Variable *cv, Mutex *lock){
|
||||
SleepConditionVariableCS(&cv->cv, &lock->crit, INFINITE);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_signal_cv(Condition_Variable *cv, Mutex *lock){
|
||||
WakeConditionVariable(&cv->cv);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_init_semaphore(Semaphore *s, u32 max){
|
||||
s->s = CreateSemaphore(0, 0, max, 0);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_wait_on_semaphore(Semaphore *s){
|
||||
WaitForSingleObject(s->s, INFINITE);
|
||||
}
|
||||
|
||||
internal void
|
||||
system_release_semaphore(Semaphore *s){
|
||||
ReleaseSemaphore(s->s, 1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// BOTTOM
|
||||
|
Loading…
Reference in New Issue