From dbb0c1e0a45eb3d4821b6726e705eb7af14eec71 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Thu, 28 Mar 2024 09:36:09 -0700 Subject: [PATCH] file_stream: solidify retry mechanism; df: multiline controls for text_cache-based info, begin porting over txti-based systems --- src/base/base_core.h | 1 + src/df/gfx/df_gfx.c | 177 ++++++++++++++++++++++++++++++++++ src/df/gfx/df_gfx.h | 1 + src/df/gfx/df_views.c | 3 +- src/file_stream/file_stream.c | 78 ++++++++++----- src/file_stream/file_stream.h | 2 +- src/hash_store/hash_store.c | 7 +- src/text_cache/text_cache.c | 11 +++ src/text_cache/text_cache.h | 1 + 9 files changed, 252 insertions(+), 29 deletions(-) diff --git a/src/base/base_core.h b/src/base/base_core.h index f0fddea2..c66772c1 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -168,6 +168,7 @@ # define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((volatile __int64 *)(x)) # define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((volatile __int64 *)(x),(c)) # define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((volatile __int64 *)(x), c) +# define ins_atomic_u32_eval(x,c) InterlockedAdd((volatile LONG *)(x), 0) # define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((volatile LONG *)(x),(c)) # define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x),(k),(c)) # define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u64_eval_assign((volatile __int64 *)(x), (__int64)(c)) diff --git a/src/df/gfx/df_gfx.c b/src/df/gfx/df_gfx.c index ec29bafd..459b55a8 100644 --- a/src/df/gfx/df_gfx.c +++ b/src/df/gfx/df_gfx.c @@ -10570,6 +10570,183 @@ df_code_slicef(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF return sig; } +internal B32 +df_do_txt_controls(TXT_TextInfo *info, String8 data, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column) +{ + Temp scratch = scratch_begin(0, 0); + B32 change = 0; + UI_NavActionList *nav_actions = ui_nav_actions(); + for(UI_NavActionNode *n = nav_actions->first, *next = 0; n != 0; n = next) + { + next = n->next; + B32 taken = 0; + + String8 line = txt_string_from_info_data_line_num(info, data, cursor->line); + UI_NavTxtOp single_line_op = ui_nav_single_line_txt_op_from_action(scratch.arena, n->v, line, *cursor, *mark); + + //- rjf: invalid single-line op or endpoint units => try multiline + if(n->v.delta_unit == UI_NavDeltaUnit_EndPoint || single_line_op.flags & UI_NavTxtOpFlag_Invalid) + { + U64 line_count = info->lines_count; + String8 prev_line = txt_string_from_info_data_line_num(info, data, cursor->line-1); + String8 next_line = txt_string_from_info_data_line_num(info, data, cursor->line+1); + Vec2S32 delta = n->v.delta; + + //- rjf: wrap lines right + if(n->v.delta_unit != UI_NavDeltaUnit_EndPoint && delta.x > 0 && cursor->column == line.size+1 && cursor->line+1 <= line_count) + { + cursor->line += 1; + cursor->column = 1; + *preferred_column = 1; + change = 1; + taken = 1; + } + + //- rjf: wrap lines left + if(n->v.delta_unit != UI_NavDeltaUnit_EndPoint && delta.x < 0 && cursor->column == 1 && cursor->line-1 >= 1) + { + cursor->line -= 1; + cursor->column = prev_line.size+1; + *preferred_column = prev_line.size+1; + change = 1; + taken = 1; + } + + //- rjf: movement down (plain) + if(n->v.delta_unit == UI_NavDeltaUnit_Element && delta.y > 0 && cursor->line+1 <= line_count) + { + cursor->line += 1; + cursor->column = Min(*preferred_column, next_line.size+1); + change = 1; + taken = 1; + } + + //- rjf: movement up (plain) + if(n->v.delta_unit == UI_NavDeltaUnit_Element && delta.y < 0 && cursor->line-1 >= 1) + { + cursor->line -= 1; + cursor->column = Min(*preferred_column, prev_line.size+1); + change = 1; + taken = 1; + } + + //- rjf: movement down (chunk) + if(n->v.delta_unit == UI_NavDeltaUnit_Chunk && delta.y > 0 && cursor->line+1 <= line_count) + { + for(S64 line_num = cursor->line+1; line_num <= line_count; line_num += 1) + { + String8 line = txt_string_from_info_data_line_num(info, data, line_num); + U64 line_size = line.size; + if(line_size == 0) + { + cursor->line = line_num; + cursor->column = 1; + break; + } + else if(line_num == line_count) + { + cursor->line = line_num; + cursor->column = line_size+1; + } + } + change = 1; + taken = 1; + } + + //- rjf: movement up (chunk) + if(n->v.delta_unit == UI_NavDeltaUnit_Chunk && delta.y < 0 && cursor->line-1 >= 1) + { + for(S64 line_num = cursor->line-1; line_num > 0; line_num -= 1) + { + String8 line = txt_string_from_info_data_line_num(info, data, line_num); + U64 line_size = line.size; + if(line_size == 0) + { + cursor->line = line_num; + cursor->column = 1; + break; + } + else if(line_num == 1) + { + cursor->line = line_num; + cursor->column = 1; + } + } + change = 1; + taken = 1; + } + + //- rjf: movement down (page) + if(n->v.delta_unit == UI_NavDeltaUnit_Whole && delta.y > 0) + { + cursor->line += line_count_per_page; + cursor->column = 1; + cursor->line = Clamp(1, cursor->line, line_count); + change = 1; + taken = 1; + } + + //- rjf: movement up (page) + if(n->v.delta_unit == UI_NavDeltaUnit_Whole && delta.y < 0) + { + cursor->line -= line_count_per_page; + cursor->column = 1; + cursor->line = Clamp(1, cursor->line, line_count); + change = 1; + taken = 1; + } + + //- rjf: movement to endpoint (+) + if(n->v.delta_unit == UI_NavDeltaUnit_EndPoint && (delta.y > 0 || delta.x > 0)) + { + *cursor = txt_pt(line_count, info->lines_count ? dim_1u64(info->lines_ranges[info->lines_count-1])+1 : 1); + change = 1; + taken = 1; + } + + //- rjf: movement to endpoint (-) + if(n->v.delta_unit == UI_NavDeltaUnit_EndPoint && (delta.y < 0 || delta.x < 0)) + { + *cursor = txt_pt(1, 1); + change = 1; + taken = 1; + } + + //- rjf: stick mark to cursor, when we don't want to keep it in the same spot + if(!(n->v.flags & UI_NavActionFlag_KeepMark)) + { + *mark = *cursor; + } + } + + //- rjf: valid single-line op => do single-line op + else + { + *cursor = single_line_op.cursor; + *mark = single_line_op.mark; + *preferred_column = cursor->column; + change = 1; + taken = 1; + } + + //- rjf: copy + if(n->v.flags & UI_NavActionFlag_Copy) + { + String8 text = txt_string_from_info_data_txt_rng(info, data, txt_rng(*cursor, *mark)); + os_set_clipboard_text(text); + } + + //- rjf: consume + if(taken) + { + ui_nav_eat_action_node(nav_actions, n); + } + } + + scratch_end(scratch); + return change; +} + internal B32 df_do_txti_controls(TXTI_Handle handle, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column) { diff --git a/src/df/gfx/df_gfx.h b/src/df/gfx/df_gfx.h index 421084b5..d6250984 100644 --- a/src/df/gfx/df_gfx.h +++ b/src/df/gfx/df_gfx.h @@ -1046,6 +1046,7 @@ internal UI_BOX_CUSTOM_DRAW(df_bp_box_draw_extensions); internal DF_CodeSliceSignal df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, String8 string); internal DF_CodeSliceSignal df_code_slicef(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, char *fmt, ...); +internal B32 df_do_txt_controls(TXT_TextInfo *info, String8 data, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column); internal B32 df_do_txti_controls(TXTI_Handle handle, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column); internal B32 df_do_dasm_controls(DASM_Handle handle, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column); diff --git a/src/df/gfx/df_views.c b/src/df/gfx/df_views.c index cf4e8911..d543c9ec 100644 --- a/src/df/gfx/df_views.c +++ b/src/df/gfx/df_views.c @@ -5503,8 +5503,7 @@ DF_VIEW_UI_FUNCTION_DEF(Code) { if(text_info_is_ready && visible_line_num_range.max >= visible_line_num_range.min && ui_is_focus_active()) { - // TODO(rjf): @txt - //snap[Axis2_X] = snap[Axis2_Y] = df_do_txti_controls(txti_handle, ClampBot(num_possible_visible_lines, 10) - 10, &tv->cursor, &tv->mark, &tv->preferred_column); + snap[Axis2_X] = snap[Axis2_Y] = df_do_txt_controls(&text_info, data, ClampBot(num_possible_visible_lines, 10) - 10, &tv->cursor, &tv->mark, &tv->preferred_column); } } diff --git a/src/file_stream/file_stream.c b/src/file_stream/file_stream.c index 8120912d..e1b411e5 100644 --- a/src/file_stream/file_stream.c +++ b/src/file_stream/file_stream.c @@ -73,10 +73,10 @@ fs_hash_from_path(String8 path, U64 endt_us) SLLQueuePush(slot->first, slot->last, node); node->path = push_str8_copy(stripe->arena, path); } - if(os_now_microseconds() >= ins_atomic_u64_eval(&node->last_time_requested_us)+1000000 && - fs_u2s_enqueue_path(path, endt_us)) + if(!ins_atomic_u32_eval_cond_assign(&node->is_working, 1, 0) && + !fs_u2s_enqueue_path(path, endt_us)) { - ins_atomic_u64_eval_assign(&node->last_time_requested_us, os_now_microseconds()); + ins_atomic_u32_eval_assign(&node->is_working, 0); } result = hs_hash_from_key(path_key, 0); if(u128_match(result, u128_zero()) && os_now_microseconds() <= endt_us) @@ -161,47 +161,75 @@ fs_streamer_thread__entry_point(void *p) for(;;) { Temp scratch = scratch_begin(0, 0); + + //- rjf: unpack path String8 path = fs_u2s_dequeue_path(scratch.arena); + U128 key = hs_hash_from_data(path); + U64 slot_idx = key.u64[0]%fs_shared->slots_count; + U64 stripe_idx = slot_idx%fs_shared->stripes_count; + FS_Slot *slot = &fs_shared->slots[slot_idx]; + FS_Stripe *stripe = &fs_shared->stripes[stripe_idx]; + + //- rjf: load + ProfBegin("load \"%.*s\"", str8_varg(path)); FileProperties pre_props = os_properties_from_file_path(path); OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, path); U64 data_arena_size = pre_props.size+ARENA_HEADER_SIZE; data_arena_size += KB(4)-1; data_arena_size -= data_arena_size%KB(4); + ProfBegin("allocate"); Arena *data_arena = arena_alloc__sized(data_arena_size, data_arena_size); + ProfEnd(); + ProfBegin("read"); String8 data = os_string_from_file_range(data_arena, file, r1u64(0, pre_props.size)); + ProfEnd(); os_file_close(file); FileProperties post_props = os_properties_from_file_path(path); + + //- rjf: abort if modification timestamps differ - we did not successfully read the file if(pre_props.modified != post_props.modified) { - arena_release(data_arena); - MemoryZeroStruct(&data); + ProfScope("abort") + { + arena_release(data_arena); + MemoryZeroStruct(&data); + data_arena = 0; + } } + + //- rjf: submit else { - U128 key = hs_hash_from_data(path); - hs_submit_data(key, &data_arena, data); - U64 slot_idx = key.u64[0]%fs_shared->slots_count; - U64 stripe_idx = slot_idx%fs_shared->stripes_count; - FS_Slot *slot = &fs_shared->slots[slot_idx]; - FS_Stripe *stripe = &fs_shared->stripes[stripe_idx]; - OS_MutexScopeW(stripe->rw_mutex) + ProfScope("submit") { - FS_Node *node = 0; - for(FS_Node *n = slot->first; n != 0; n = n->next) + hs_submit_data(key, &data_arena, data); + } + } + + //- rjf: commit info to cache + ProfScope("commit to cache") OS_MutexScopeW(stripe->rw_mutex) + { + FS_Node *node = 0; + for(FS_Node *n = slot->first; n != 0; n = n->next) + { + if(str8_match(n->path, path, 0)) { - if(str8_match(n->path, path, 0)) - { - node = n; - break; - } + node = n; + break; } - if(node != 0) + } + if(node != 0) + { + if(post_props.modified == pre_props.modified) { node->timestamp = post_props.modified; } + ins_atomic_u32_eval_assign(&node->is_working, 0); } - os_condition_variable_broadcast(stripe->cv); } + os_condition_variable_broadcast(stripe->cv); + + ProfEnd(); scratch_end(scratch); } } @@ -226,9 +254,13 @@ fs_detector_thread__entry_point(void *p) for(FS_Node *n = slot->first; n != 0; n = n->next) { FileProperties props = os_properties_from_file_path(n->path); - if(props.modified != n->timestamp && n->last_time_requested_us+100000 < os_now_microseconds()) + if(props.modified != n->timestamp) { - fs_u2s_enqueue_path(n->path, os_now_microseconds()+100000); + if(!ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0) && + !fs_u2s_enqueue_path(n->path, os_now_microseconds()+100000)) + { + ins_atomic_u32_eval_assign(&n->is_working, 0); + } } } } diff --git a/src/file_stream/file_stream.h b/src/file_stream/file_stream.h index 6a491e44..078b3547 100644 --- a/src/file_stream/file_stream.h +++ b/src/file_stream/file_stream.h @@ -13,7 +13,7 @@ struct FS_Node FS_Node *next; String8 path; U64 timestamp; - U64 last_time_requested_us; + B32 is_working; }; typedef struct FS_Slot FS_Slot; diff --git a/src/hash_store/hash_store.c b/src/hash_store/hash_store.c index 5a9570c6..501d888a 100644 --- a/src/hash_store/hash_store.c +++ b/src/hash_store/hash_store.c @@ -84,7 +84,7 @@ hs_submit_data(U128 key, Arena **data_arena, String8 data) HS_Stripe *stripe = &hs_shared->stripes[stripe_idx]; //- rjf: commit data to cache - if already there, just bump key refcount - OS_MutexScopeW(stripe->rw_mutex) + ProfScope("commit data to cache - if already there, just bump key refcount") OS_MutexScopeW(stripe->rw_mutex) { HS_Node *existing_node = 0; for(HS_Node *n = slot->first; n != 0; n = n->next) @@ -123,7 +123,7 @@ hs_submit_data(U128 key, Arena **data_arena, String8 data) //- rjf: commit this hash to key cache U128 key_expired_hash = {0}; - OS_MutexScopeW(key_stripe->rw_mutex) + ProfScope("commit this hash to key cache") OS_MutexScopeW(key_stripe->rw_mutex) { HS_KeyNode *key_node = 0; for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next) @@ -152,7 +152,8 @@ hs_submit_data(U128 key, Arena **data_arena, String8 data) } //- rjf: if this key's history cache was full, dec key ref count of oldest hash - if(!u128_match(key_expired_hash, u128_zero())) + ProfScope("if this key's history cache was full, dec key ref count of oldest hash") + if(!u128_match(key_expired_hash, u128_zero())) { U64 old_hash_slot_idx = key_expired_hash.u64[1]%hs_shared->slots_count; U64 old_hash_stripe_idx = old_hash_slot_idx%hs_shared->stripes_count; diff --git a/src/text_cache/text_cache.c b/src/text_cache/text_cache.c index 9252c172..6d06b1ea 100644 --- a/src/text_cache/text_cache.c +++ b/src/text_cache/text_cache.c @@ -1037,6 +1037,17 @@ txt_string_from_info_data_txt_rng(TXT_TextInfo *info, String8 data, TxtRng rng) return result; } +internal String8 +txt_string_from_info_data_line_num(TXT_TextInfo *info, String8 data, S64 line_num) +{ + String8 result = {0}; + if(1 <= line_num && line_num <= info->lines_count) + { + result = str8_substr(data, info->lines_ranges[line_num-1]); + } + return result; +} + internal TXT_LineTokensSlice txt_line_tokens_slice_from_info_data_line_range(Arena *arena, TXT_TextInfo *info, String8 data, Rng1S64 line_range) { diff --git a/src/text_cache/text_cache.h b/src/text_cache/text_cache.h index 43e6f41a..a3390f06 100644 --- a/src/text_cache/text_cache.h +++ b/src/text_cache/text_cache.h @@ -279,6 +279,7 @@ internal TXT_TokenArray txt_token_array_from_info_line_num__linear_scan(TXT_Text internal Rng1U64 txt_expr_off_range_from_line_off_range_string_tokens(U64 off, Rng1U64 line_range, String8 line_text, TXT_TokenArray *line_tokens); internal Rng1U64 txt_expr_off_range_from_info_data_pt(TXT_TextInfo *info, String8 data, TxtPt pt); internal String8 txt_string_from_info_data_txt_rng(TXT_TextInfo *info, String8 data, TxtRng rng); +internal String8 txt_string_from_info_data_line_num(TXT_TextInfo *info, String8 data, S64 line_num); internal TXT_LineTokensSlice txt_line_tokens_slice_from_info_data_line_range(Arena *arena, TXT_TextInfo *info, String8 data, Rng1S64 line_range); ////////////////////////////////