From ac04842f97282bb6d69463b098333a4dac04d4c2 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Mon, 12 Aug 2019 21:19:02 -0700 Subject: [PATCH] New file change notification system --- 4ed.cpp | 101 ++++++++------------- 4ed_app_models.h | 20 ++-- 4ed_file.cpp | 6 +- 4ed_file.h | 7 +- 4ed_system.h | 8 ++ 4ed_working_set.cpp | 60 +++++++++++- 4ed_working_set.h | 17 +++- platform_all/4ed_link_system_functions.cpp | 2 + platform_win32/win32_4ed.cpp | 17 +++- platform_win32/win32_4ed_functions.cpp | 4 +- 10 files changed, 154 insertions(+), 88 deletions(-) diff --git a/4ed.cpp b/4ed.cpp index 17d06a3a..a1350165 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -12,6 +12,24 @@ #define DEFAULT_DISPLAY_WIDTH 672 #define DEFAULT_MINIMUM_BASE_DISPLAY_WIDTH 550 +//////////////////////////////// + +Mutex_Lock::Mutex_Lock(System_Functions *s, System_Mutex m){ + s->mutex_acquire(m); + this->system = s; + this->mutex = m; +} + +Mutex_Lock::~Mutex_Lock(){ + this->system->mutex_release(this->mutex); +} + +Mutex_Lock::operator System_Mutex(){ + return(this->mutex); +} + +//////////////////////////////// + internal App_Coroutine_State get_state(Application_Links *app){ App_Coroutine_State state = {}; @@ -732,15 +750,15 @@ make_arena_models(Models *models){ //////////////////////////////// -internal App_Vars* +internal Models* app_setup_memory(System_Functions *system, Application_Memory *memory){ Cursor cursor = make_cursor(memory->vars_memory, memory->vars_memory_size); - App_Vars *vars = push_array_zero(&cursor, App_Vars, 1); - vars->models.mem.arena = make_arena_system(system); - vars->models.base_allocator = vars->models.mem.arena.base_allocator; - heap_init(&vars->models.mem.heap); - heap_extend(&vars->models.mem.heap, memory->target_memory, memory->target_memory_size); - return(vars); + Models *models = push_array_zero(&cursor, Models, 1); + models->mem.arena = make_arena_system(system); + models->base_allocator = models->mem.arena.base_allocator; + heap_init(&models->mem.heap); + heap_extend(&models->mem.heap, memory->target_memory, memory->target_memory_size); + return(models); } internal u32 @@ -822,21 +840,21 @@ launch_command_via_keycode(System_Functions *system, Models *models, View *view, App_Read_Command_Line_Sig(app_read_command_line){ i32 out_size = 0; - App_Vars *vars = app_setup_memory(system, memory); - App_Settings *settings = &vars->models.settings; + Models *models = app_setup_memory(system, memory); + App_Settings *settings = &models->settings; memset(settings, 0, sizeof(*settings)); plat_settings->font_size = 16; if (argc > 1){ - init_command_line_settings(&vars->models.settings, plat_settings, argc, argv); + init_command_line_settings(&models->settings, plat_settings, argc, argv); } - *files = vars->models.settings.init_files; - *file_count = &vars->models.settings.init_files_count; + *files = models->settings.init_files; + *file_count = &models->settings.init_files_count; return(out_size); } App_Init_Sig(app_init){ - App_Vars *vars = (App_Vars*)memory->vars_memory; - Models *models = &vars->models; + Models *models = (Models*)memory->vars_memory; + models->system = system; models->keep_playing = true; app_links_init(system, &models->app_links, memory->user_memory, memory->user_memory_size); @@ -893,7 +911,10 @@ App_Init_Sig(app_init){ dynamic_workspace_init(&models->mem.heap, &models->lifetime_allocator, DynamicWorkspace_Global, 0, &models->dynamic_workspace); // NOTE(allen): file setup - working_set_init(system, &models->working_set); + working_set_init(models, &models->working_set); + + Mutex_Lock file_order_lock(system, models->working_set.mutex); + models->working_set.default_display_width = DEFAULT_DISPLAY_WIDTH; models->working_set.default_minimum_base_display_width = DEFAULT_MINIMUM_BASE_DISPLAY_WIDTH; @@ -929,10 +950,6 @@ App_Init_Sig(app_init){ models->title_space = push_array(arena, char, models->title_capacity); block_copy(models->title_space, WINDOW_NAME, sizeof(WINDOW_NAME)); - // NOTE(allen): init system context - models->system = system; - models->vars = vars; - // NOTE(allen): init baked in buffers File_Init init_files[] = { { string_u8_litinit("*messages*"), &models->message_buffer, true , }, @@ -978,6 +995,8 @@ App_Init_Sig(app_init){ App_Step_Sig(app_step){ Models *models = (Models*)memory->vars_memory; + Mutex_Lock file_order_lock(system, models->working_set.mutex); + models->next_animate_delay = max_u32; models->animate_next_frame = false; @@ -996,50 +1015,6 @@ App_Step_Sig(app_step){ } } -#if 0 - // NOTE(allen): check files are up to date - { - b32 mem_too_small = 0; - i32 size = 0; - i32 buffer_size = KB(32); - - Arena *scratch = &models->mem.arena; - Temp_Memory temp = begin_temp(scratch); - char *buffer = push_array(scratch, char, buffer_size); - u32 unmark_top = 0; - u32 unmark_max = Thousand(8); - Editing_File **unmark = (Editing_File**)push_array(scratch, Editing_File*, unmark_max); - - Working_Set *working_set = &models->working_set; - - for (;system->get_file_change(buffer, buffer_size, &mem_too_small, &size);){ - Assert(!mem_too_small); - Editing_File_Name canon = {}; - if (get_canon_name(system, scratch, SCu8(buffer, size), &canon)){ - Editing_File *file = working_set_contains_canon(working_set, string_from_file_name(&canon)); - if (file != 0){ - if (file->state.ignore_behind_os == 0){ - file_add_dirty_flag(file, DirtyState_UnloadedChanges); - } - else if (file->state.ignore_behind_os == 1){ - file->state.ignore_behind_os = 2; - unmark[unmark_top++] = file; - if (unmark_top == unmark_max){ - break; - } - } - } - } - } - - for (u32 i = 0; i < unmark_top; ++i){ - unmark[i]->state.ignore_behind_os = 0; - } - - end_temp(temp); - } -#endif - // NOTE(allen): reorganizing panels on screen Vec2_i32 prev_dim = layout_get_root_size(&models->layout); Vec2_i32 current_dim = V2i32(target->width, target->height); diff --git a/4ed_app_models.h b/4ed_app_models.h index 33438ed4..2d3a57e3 100644 --- a/4ed_app_models.h +++ b/4ed_app_models.h @@ -35,6 +35,7 @@ enum App_State{ }; struct Models{ + System_Functions *system; Base_Allocator *base_allocator; Mem_Options mem; @@ -121,10 +122,6 @@ struct Models{ b32 animated_last_frame; u64 last_render_usecond_stamp; - // System Context - System_Functions *system; - struct App_Vars *vars; - // Event Context Application_Step_Input *input; Key_Event_Data key; @@ -162,11 +159,6 @@ struct Consumption_Record{ char consumer[32]; }; -// TODO(allen): GET RID OF IT! -struct App_Vars{ - Models models; -}; - typedef i32 App_Coroutine_Purpose; enum{ Co_View, @@ -217,6 +209,16 @@ enum{ AppCoroutineRequest_ModifyFace = 2, }; +//////////////////////////////// + +struct Mutex_Lock{ + Mutex_Lock(System_Functions *system, System_Mutex mutex); + ~Mutex_Lock(); + operator System_Mutex(); + System_Functions *system; + System_Mutex mutex; +}; + #endif // BOTTOM diff --git a/4ed_file.cpp b/4ed_file.cpp index 388be730..60745332 100644 --- a/4ed_file.cpp +++ b/4ed_file.cpp @@ -94,13 +94,13 @@ file_set_unimportant(Editing_File *file, b32 val){ if (val){ file->state.dirty = DirtyState_UpToDate; } - file->settings.unimportant = (b8)(val != false); + file->settings.unimportant = (b8)(val); } internal void file_set_to_loading(Editing_File *file){ - memset(&file->state, 0, sizeof(file->state)); - memset(&file->settings, 0, sizeof(file->settings)); + block_zero_struct(&file->state); + block_zero_struct(&file->settings); file->is_loading = true; } diff --git a/4ed_file.h b/4ed_file.h index 021fe21e..54742ca2 100644 --- a/4ed_file.h +++ b/4ed_file.h @@ -98,17 +98,18 @@ struct Editing_File{ Node main_chain_node; }; Node touch_node; + Node reloaded_node; + Node edit_finished_mark_node; + b32 edit_finished_marked; + b32 is_loading; Buffer_ID id; Editing_File_Settings settings; - b32 is_loading; Editing_File_State state; File_Attributes attributes; Lifetime_Object *lifetime_object; Editing_File_Name base_name; Editing_File_Name unique_name; Editing_File_Name canon; - b32 edit_finished_marked; - Node edit_finished_mark_node; }; #endif diff --git a/4ed_system.h b/4ed_system.h index b045a36d..c12c9b2b 100644 --- a/4ed_system.h +++ b/4ed_system.h @@ -61,6 +61,12 @@ typedef Sys_Wake_Up_Timer_Set_Sig(System_Wake_Up_Timer_Set); #define Sys_Wake_Up_Timer_Check_Sig(name) u64 name(Plat_Handle handle) typedef Sys_Wake_Up_Timer_Check_Sig(System_Wake_Up_Timer_Check); +#define Sys_Signal_Step_Sig(name) void name(u32 code) +typedef Sys_Signal_Step_Sig(System_Signal_Step); + +#define Sys_Sleep_Sig(name) void name(u64 microseconds) +typedef Sys_Sleep_Sig(System_Sleep); + // clipboard #define Sys_Post_Clipboard_Sig(name) void name(String_Const_u8 str) typedef Sys_Post_Clipboard_Sig(System_Post_Clipboard); @@ -179,6 +185,8 @@ struct System_Functions{ 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; + System_Signal_Step *signal_step; + System_Sleep *sleep; // clipboard System_Post_Clipboard *post_clipboard; diff --git a/4ed_working_set.cpp b/4ed_working_set.cpp index 4790510e..cd6f3b09 100644 --- a/4ed_working_set.cpp +++ b/4ed_working_set.cpp @@ -19,6 +19,54 @@ working_set_file_default_settings(Working_Set *working_set, Editing_File *file){ //////////////////////////////// +internal void +file_change_notification_check(System_Functions *system, Working_Set *working_set, Editing_File *file){ + if (file->canon.name_size > 0 && !file->settings.unimportant){ + String_Const_u8 name = SCu8(file->canon.name_space, file->canon.name_size); + File_Attributes attributes = system->quick_file_attributes(name); + if (attributes.last_write_time > file->attributes.last_write_time){ + if (!HasFlag(file->state.dirty, DirtyState_UnloadedChanges)){ + file_add_dirty_flag(file, DirtyState_UnloadedChanges); + dll_insert_back(&working_set->has_reloaded_sentinel, + &file->reloaded_node); + system->signal_step(0); + } + } + file->attributes = attributes; + } +} + +internal void +file_change_notification_thread_main(void *ptr){ + Models *models = (Models*)ptr; + System_Functions *system = models->system; + Working_Set *working_set = &models->working_set; + for (;;){ + system->sleep(Thousand(250)); + Mutex_Lock lock(system, working_set->mutex); + if (working_set->active_file_count > 0){ + i32 check_count = working_set->active_file_count/16; + check_count = clamp(1, check_count, 100); + Node *used = &working_set->active_file_sentinel; + Node *node = working_set->sync_check_iterator; + if (node == 0 || node == used){ + node = used->next; + } + for (i32 i = 0; i < check_count; i += 1){ + Editing_File *file = CastFromMember(Editing_File, main_chain_node, node); + node = node->next; + if (node == used){ + node = node->next; + } + file_change_notification_check(system, working_set, file); + } + working_set->sync_check_iterator = node; + } + } +} + +//////////////////////////////// + internal Editing_File* working_set_allocate_file(Working_Set *working_set, Lifetime_Allocator *lifetime_allocator){ Editing_File *file = working_set->free_files; @@ -46,7 +94,10 @@ working_set_allocate_file(Working_Set *working_set, Lifetime_Allocator *lifetime } internal void -working_set_free_file(Heap *heap, Working_Set *working_set, Editing_File *file){ +working_set_free_file(Heap *heap, Working_Set *working_set, Editing_File *file){ + if (working_set->sync_check_iterator == &file->main_chain_node){ + working_set->sync_check_iterator = working_set->sync_check_iterator->next; + } dll_remove(&file->main_chain_node); dll_remove(&file->touch_node); working_set->active_file_count -= 1; @@ -65,8 +116,9 @@ working_set_get_file(Working_Set *working_set, Buffer_ID id){ } internal void -working_set_init(System_Functions *system, Working_Set *working_set){ +working_set_init(Models *models, Working_Set *working_set){ block_zero_struct(working_set); + System_Functions *system = models->system; working_set->arena = make_arena_system(system); working_set->id_counter = 1; @@ -82,6 +134,10 @@ working_set_init(System_Functions *system, Working_Set *working_set){ working_set->id_to_ptr_table = make_table_u64_u64(allocator, slot_count); working_set->canon_table = make_table_Data_u64(allocator, slot_count); working_set->name_table = make_table_Data_u64(allocator, slot_count); + + dll_init_sentinel(&working_set->has_reloaded_sentinel); + working_set->mutex = system->mutex_make(); + working_set->file_change_thread = system->thread_launch(file_change_notification_thread_main, models); } internal Editing_File* diff --git a/4ed_working_set.h b/4ed_working_set.h index 6859372d..09458e70 100644 --- a/4ed_working_set.h +++ b/4ed_working_set.h @@ -13,6 +13,10 @@ #define FRED_WORKING_SET_H struct Working_Set{ + // NOTE(allen): After initialization of file_change_thread + // the members of this struct should only be accessed by a thread + // who owns the mutex member. + Arena arena; Editing_File *free_files; @@ -32,15 +36,22 @@ struct Working_Set{ Table_Data_u64 canon_table; Table_Data_u64 name_table; + Node *sync_check_iterator; + Node has_reloaded_sentinel; + System_Mutex mutex; + System_Thread file_change_thread; + + i32 default_display_width; + i32 default_minimum_base_display_width; + // TODO(allen): do(update clipboard system to exist fully in the custom layer) + // NOTE(allen): These members have nothing to do with the working set or + // the mutex that gaurds the other members. String_Const_u8 clipboards[64]; i32 clipboard_size; i32 clipboard_max_size; i32 clipboard_current; i32 clipboard_rolling; - - i32 default_display_width; - i32 default_minimum_base_display_width; }; internal void diff --git a/platform_all/4ed_link_system_functions.cpp b/platform_all/4ed_link_system_functions.cpp index f7eb7837..1b7897e7 100644 --- a/platform_all/4ed_link_system_functions.cpp +++ b/platform_all/4ed_link_system_functions.cpp @@ -28,6 +28,8 @@ link_system_code(void){ SYSLINK(wake_up_timer_create); SYSLINK(wake_up_timer_release); SYSLINK(wake_up_timer_set); + SYSLINK(signal_step); + SYSLINK(sleep); SYSLINK(post_clipboard); diff --git a/platform_win32/win32_4ed.cpp b/platform_win32/win32_4ed.cpp index c2aa1fdb..88adb8fa 100644 --- a/platform_win32/win32_4ed.cpp +++ b/platform_win32/win32_4ed.cpp @@ -270,8 +270,8 @@ handle_type_ptr(void *ptr){ //////////////////////////////// internal void -system_schedule_step(){ - PostMessage(win32vars.window_handle, WM_4coder_ANIMATE, 0, 0); +system_schedule_step(u32 code){ + PostMessage(win32vars.window_handle, WM_4coder_ANIMATE, code, 0); } //////////////////////////////// @@ -894,6 +894,17 @@ Sys_Wake_Up_Timer_Set_Sig(system_wake_up_timer_set){ } } +internal +Sys_Signal_Step_Sig(system_signal_step){ + system_schedule_step(code); +} + +internal +Sys_Sleep_Sig(system_sleep){ + u32 milliseconds = (u32)(microseconds/Thousand(1)); + Sleep(milliseconds); +} + //////////////////////////////// internal DWORD @@ -1919,7 +1930,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS // NOTE(allen): schedule another step if needed if (result.animating){ - system_schedule_step(); + system_schedule_step(0); } // NOTE(allen): sleep a bit to cool off :) diff --git a/platform_win32/win32_4ed_functions.cpp b/platform_win32/win32_4ed_functions.cpp index 6667ed88..686f2336 100644 --- a/platform_win32/win32_4ed_functions.cpp +++ b/platform_win32/win32_4ed_functions.cpp @@ -375,7 +375,7 @@ int_color_from_colorref(COLORREF ref, int_color alpha_from){ } internal void -system_schedule_step(); +system_schedule_step(u32 code); internal UINT_PTR CALLBACK color_picker_hook(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam){ @@ -428,7 +428,7 @@ color_picker_hook(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam){ if(*picker->dest != new_color) { *picker->dest = new_color; - system_schedule_step(); + system_schedule_step(0); } } }