From: Kevin Day Date: Sun, 11 Apr 2021 05:01:36 +0000 (-0500) Subject: Progress: controller program. X-Git-Tag: 0.5.3~60 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=c72531fc5f10d7546e6c4ca8249baa0f3c19244b;p=fll Progress: controller program. I completely overlooked that the thread time related functions are relative to the absolute system clock. The timed locks were all acting crazy because I used relative values! The process status is not being initialized, resulting in invalid and error prone checks. Begin fixing problems with the asynchronous processing. Between incorrect uses of times and some stuctural problems, the asynchronous behavior was not operating in the order of the dependencies. The current work fixes that, but there is still more work to do. I expect this code to not work just yet. There also seems to be a locking issue somewhere in here that prevents the program from fully exiting. Check the thread conditions as those have had problems in the past. . --- diff --git a/level_3/controller/c/controller.h b/level_3/controller/c/controller.h index 0cc1f62..920f4d6 100644 --- a/level_3/controller/c/controller.h +++ b/level_3/controller/c/controller.h @@ -27,6 +27,8 @@ // libc includes #include #include +#include +#include #include #include #include diff --git a/level_3/controller/c/private-common.c b/level_3/controller/c/private-common.c index 888cec0..873ffc8 100644 --- a/level_3/controller/c/private-common.c +++ b/level_3/controller/c/private-common.c @@ -164,12 +164,13 @@ extern "C" { f_status_t controller_lock_write(controller_thread_t * const thread, f_thread_lock_t *lock) { struct timespec time; - time.tv_sec = 0; - time.tv_nsec = controller_thread_lock_timeout; f_status_t status = F_none; for (;;) { + + controller_time(0, controller_thread_lock_timeout, &time); + status = f_thread_lock_write_timed(&time, lock); if (status == F_time) { @@ -223,35 +224,41 @@ extern "C" { #endif // _di_controller_process_delete_simple_ #ifndef _di_controller_process_wait_ - void controller_process_wait(const controller_main_t main, controller_process_t *process) { + f_status_t controller_process_wait(const controller_main_t main, controller_process_t *process) { - if (!main.thread->enabled) return; + if (!main.thread->enabled) { + return F_signal; + } struct timespec time; - time.tv_sec = controller_thread_wait_timeout_seconds; - time.tv_nsec = controller_thread_wait_timeout_nanoseconds; f_status_t status = F_none; do { f_thread_mutex_lock(&process->wait_lock); + controller_time(controller_thread_wait_timeout_seconds, controller_thread_wait_timeout_nanoseconds, &time); + status = f_thread_condition_wait_timed(&time, &process->wait, &process->wait_lock); f_thread_mutex_unlock(&process->wait_lock); - if (!main.thread->enabled) break; + if (!main.thread->enabled) { + return F_signal; + } f_thread_lock_read(&process->lock); if (process->status != F_known_not || !(process->state == controller_process_state_active || process->state == controller_process_state_busy)) { f_thread_unlock(&process->lock); - break; + return F_none; } f_thread_unlock(&process->lock); - } while (main.thread->enabled); + } while (status == F_time && main.thread->enabled); + + return status; } #endif // _di_controller_process_wait_ @@ -330,6 +337,10 @@ extern "C" { processs->size = length; return status; } + else { + process->status = F_known_not; + process->rule.status = F_known_not; + } } // for processs->size = length; @@ -486,6 +497,18 @@ extern "C" { } #endif // _di_controller_thread_delete_simple_ +#ifndef _di_controller_time_ + void controller_time(const time_t seconds, const long nanos, struct timespec *time) { + + struct timeval now; + + gettimeofday(&now, 0); + + time->tv_sec = now.tv_sec + seconds; + time->tv_nsec = now.tv_usec * 1000 + nanos; + } +#endif // _di_controller_time_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_3/controller/c/private-common.h b/level_3/controller/c/private-common.h index 32b6321..9b2f91f 100644 --- a/level_3/controller/c/private-common.h +++ b/level_3/controller/c/private-common.h @@ -1079,7 +1079,13 @@ extern "C" { #define controller_thread_exit_process_cancel_total 150 // 9 seconds in multiples of wait. #define controller_thread_lock_timeout 100000000 // 0.1 seconds in nanoseconds. #define controller_thread_simulation_timeout 200000 // 0.2 seconds in microseconds. - #define controller_thread_wait_timeout_seconds 10 + + // @todo implement a staged incrementing wait that waits a short amount of time for say 3 times, then waits a longer amount of time for say 3 times, and then wait a long period of time. + // example: wait 0.02 seconds for 4 times, then + // wait 0.2 seconds for 8 times, then + // wait 2 seconds for 16 times, then + // wait 20 seconds for every time thereafter. + #define controller_thread_wait_timeout_seconds 5 #define controller_thread_wait_timeout_nanoseconds 0 typedef struct { @@ -1378,10 +1384,18 @@ extern "C" { * @param process * The process to wait on. * + * @return + * F_none on success. + * F_signal on success and signal found. + * + * Success from f_thread_condition_wait_timed(). + * + * Errors (with error bit) from: f_thread_condition_wait_timed(). + * * @see f_thread_condition_wait_timed() */ #ifndef _di_controller_process_wait_ - extern void controller_process_wait(const controller_main_t main, controller_process_t *process) f_gcc_attribute_visibility_internal; + extern f_status_t controller_process_wait(const controller_main_t main, controller_process_t *process) f_gcc_attribute_visibility_internal; #endif // _di_controller_process_wait_ /** @@ -1598,6 +1612,23 @@ extern "C" { extern void controller_thread_delete_simple(controller_thread_t *thread) f_gcc_attribute_visibility_internal; #endif // _di_controller_thread_delete_simple_ +/** + * Get the current time, plus the given offset. + * + * @todo this is basic enough that there needs to be an f_time class with this function f_time_now(), f_time_future(), f_time_past(). + * "struct timespec" -> f_time_nano_t, "struct timeval" -> f_time_micro_t. + * + * @param seconds + * The seconds to add to current time. + * @param nanos + * The nanoseconds to add to current time. + * @param time + * The resulting current time. + */ +#ifndef _di_controller_time_ + void controller_time(const time_t seconds, const long nanos, struct timespec *time) f_gcc_attribute_visibility_internal; +#endif // _di_controller_time_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_3/controller/c/private-rule.c b/level_3/controller/c/private-rule.c index 764f090..33304e2 100644 --- a/level_3/controller/c/private-rule.c +++ b/level_3/controller/c/private-rule.c @@ -1705,10 +1705,14 @@ extern "C" { f_array_length_t k = 0; f_array_length_t id_rule = 0; f_array_length_t id_process = 0; + bool found = F_false; + bool busy = F_false; controller_process_t *process_other = 0; + uint8_t rule_options = 0; + f_string_dynamics_t * const dynamics[] = { &process->rule.need, &process->rule.want, @@ -1727,10 +1731,19 @@ extern "C" { for (j = 0; j < dynamics[i]->used; ++j) { + process_other = 0; + found = F_false; + f_thread_lock_read(&main.thread->lock.process); status = controller_find_process(dynamics[i]->array[j], main.thread->processs, &id_process); + if (status == F_true) { + process_other = main.thread->processs.array[id_process]; + + f_thread_lock_read(&process_other->active); + } + f_thread_unlock(&main.thread->lock.process); if (status == F_true) { @@ -1762,6 +1775,10 @@ extern "C" { if (!(process->options & controller_rule_option_simulate)) { f_thread_unlock(&main.thread->lock.rule); + if (process_other) { + f_thread_unlock(&process_other->active); + } + break; } } @@ -1775,54 +1792,50 @@ extern "C" { controller_print_unlock_flush(main.data->output.stream, &main.thread->lock.print); } } - - f_thread_unlock(&main.thread->lock.rule); + } + else { + f_thread_lock_read(&main.thread->lock.rule); } - f_thread_lock_read(&main.thread->lock.rule); + if (found) { - if (status == F_true && found) { - f_thread_unlock(&main.thread->lock.rule); - f_thread_lock_read(&main.thread->lock.process); + // the process_other may have write locks, which needs to be avoided, so copy the alias from the rule. + char alias_other_buffer[main.setting->rules.array[id_rule].alias.used + 1]; - process_other = main.thread->processs.array[id_process]; + memcpy(alias_other_buffer, main.setting->rules.array[id_rule].alias.string, sizeof(char) * main.setting->rules.array[id_rule].alias.used); + alias_other_buffer[main.setting->rules.array[id_rule].alias.used] = 0; - f_thread_lock_read(&process_other->active); - f_thread_lock_read(&process_other->lock); + const f_string_static_t alias_other = f_macro_string_static_t_initialize(alias_other_buffer, main.setting->rules.array[id_rule].alias.used); - if (process_other->status == F_known_not && (process_other->state == controller_process_state_active || process_other->state == controller_process_state_busy)) { - f_thread_unlock(&process_other->lock); + f_thread_unlock(&main.thread->lock.rule); - controller_process_wait(main, process_other); + busy = F_false; + + status = f_thread_lock_read_try(&process_other->lock); + + if (status == F_busy) { + busy = F_true; } else { + if (process_other->status == F_known_not || process_other->state == controller_process_state_active || process_other->state == controller_process_state_busy) { + busy = F_true; + } + f_thread_unlock(&process_other->lock); } - f_thread_unlock(&main.thread->lock.process); - - if (!main.thread->enabled) { - f_thread_unlock(&process_other->active); - - status = F_signal; - break; + if (busy) { + controller_process_wait(main, process_other); } + else { + rule_options = 0; - f_thread_lock_read(&main.thread->lock.rule); - - char alias_other_buffer[main.setting->rules.array[id_rule].alias.used + 1]; - - memcpy(alias_other_buffer, main.setting->rules.array[id_rule].alias.string, sizeof(char) * main.setting->rules.array[id_rule].alias.used); - alias_other_buffer[main.setting->rules.array[id_rule].alias.used] = 0; - - // attempt to (synchronously) execute the rule when the status is unknown (the rule has not yet been run). - if (main.setting->rules.array[id_rule].status == F_known_not) { - - const f_string_static_t alias_other = f_macro_string_static_t_initialize(alias_other_buffer, main.setting->rules.array[id_rule].alias.used); - - f_thread_unlock(&main.thread->lock.rule); + if (main.data->parameters[controller_parameter_test].result == f_console_result_found) { + rule_options |= controller_rule_option_simulate; + } - status = controller_rule_process_begin(controller_process_option_execute, alias_other, action, process->options & controller_rule_option_asynchronous ? process->options - controller_rule_option_asynchronous : process->options, process->stack, main, process->cache); + // synchronously execute dependency. + status = controller_rule_process_begin(controller_process_option_execute, alias_other, controller_rule_action_type_start, controller_process_option_execute, process->stack, main, process_other->cache); if (status == F_child || status == F_signal) { f_thread_unlock(&process_other->active); @@ -1839,7 +1852,7 @@ extern "C" { controller_print_unlock_flush(main.data->output.stream, &main.thread->lock.print); - if (!(process->options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) { + if (!(process_other->options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) { f_thread_unlock(&process_other->active); break; @@ -1856,10 +1869,16 @@ extern "C" { } } } + } - f_thread_lock_read(&main.thread->lock.rule); + if (!main.thread->enabled) { + f_thread_unlock(&process_other->active); + + break; } + f_thread_lock_read(&main.thread->lock.rule); + if (F_status_is_error(main.setting->rules.array[id_rule].status)) { if (i == 0 || i == 1) { f_thread_mutex_lock(&main.thread->lock.print); @@ -1871,9 +1890,13 @@ extern "C" { controller_print_unlock_flush(main.data->output.stream, &main.thread->lock.print); - if (!(process->options & controller_rule_option_simulate)) { + if (!(process_other->options & controller_rule_option_simulate)) { f_thread_unlock(&main.thread->lock.rule); - f_thread_unlock(&process_other->active); + + if (process_other) { + f_thread_unlock(&process_other->active); + } + break; } } @@ -1890,12 +1913,15 @@ extern "C" { } f_thread_unlock(&main.thread->lock.rule); - f_thread_unlock(&process_other->active); } else { f_thread_unlock(&main.thread->lock.rule); } + if (process_other) { + f_thread_unlock(&process_other->active); + } + if (!main.thread->enabled) break; } // for @@ -1915,8 +1941,8 @@ extern "C" { return F_signal; } - if (!(process->options & controller_rule_option_wait) && F_status_is_error_not(status)) { - controller_rule_wait_all(main, process); + if ((process->options & controller_rule_option_wait) && F_status_is_error_not(status)) { + controller_rule_wait_all(main, process); // @fixme review this, it needs to check anything depending on itself! if (!main.thread->enabled) { return F_signal; @@ -2497,6 +2523,11 @@ extern "C" { process->stack.used = used_original_stack; + // inform all things waiting that the process has finished running. + f_thread_mutex_lock(&process->wait_lock); + f_thread_condition_signal_all(&process->wait); + f_thread_mutex_unlock(&process->wait_lock); + f_thread_unlock(&process->lock); if (options & controller_process_option_asynchronous) { @@ -2504,19 +2535,10 @@ extern "C" { } if (main.thread->enabled) { - if (options & controller_process_option_execute) { - - // inform all things waiting that the process has finished running. - f_thread_mutex_lock(&process->wait_lock); - f_thread_condition_signal_all(&process->wait); - f_thread_mutex_unlock(&process->wait_lock); - } - } - else { - return F_signal; + return status; } - return status; + return F_signal; } #endif // _di_controller_rule_process_do_ @@ -4888,7 +4910,7 @@ extern "C" { if (caller) { f_thread_lock_read(&main.thread->lock.rule); - for (j = 0; j < caller->stack.used; ++j) { + for (j = 0; j < caller->stack.used && main.thread->enabled; ++j) { if (main.thread->processs.array[caller->stack.array[j]] && fl_string_dynamic_compare(process->rule.alias, main.thread->processs.array[caller->stack.array[j]]->rule.alias) == F_equal_to) { skip = F_true; @@ -4899,6 +4921,7 @@ extern "C" { f_thread_unlock(&main.thread->lock.rule); + if (!main.thread->enabled) break; if (skip) continue; } @@ -4927,16 +4950,12 @@ extern "C" { } if (process->state == controller_process_state_done) { - f_thread_unlock(&process->active); - if (f_thread_lock_write_try(&process->active) == F_none) { f_thread_join(process->id_thread, 0); + process->state = controller_process_state_idle; process->id_thread = 0; } - else { - f_thread_lock_read(&process->active); - } } } diff --git a/level_3/controller/c/private-thread.c b/level_3/controller/c/private-thread.c index bb23ee3..bd273bb 100644 --- a/level_3/controller/c/private-thread.c +++ b/level_3/controller/c/private-thread.c @@ -350,9 +350,7 @@ extern "C" { f_status_t status = F_none; f_array_length_t spent = 0; - struct timespec wait; - wait.tv_sec = 0; - wait.tv_nsec = controller_thread_exit_process_cancel_wait; + struct timespec time; controller_process_t *process = 0; @@ -388,7 +386,9 @@ extern "C" { process = main->thread->processs.array[i]; if (process->id_thread) { - status = f_thread_join_timed(process->id_thread, wait, 0); + controller_time(0, controller_thread_exit_process_cancel_wait, &time); + + status = f_thread_join_timed(process->id_thread, time, 0); if (status == F_none) { process->child = 0; @@ -407,7 +407,9 @@ extern "C" { process = main->thread->processs.array[i]; do { - status = f_thread_join_timed(process->id_thread, wait, 0); + controller_time(0, controller_thread_exit_process_cancel_wait, &time); + + status = f_thread_join_timed(process->id_thread, time, 0); if (status == F_none) { process->child = 0; @@ -429,7 +431,10 @@ extern "C" { if (process->child > 0) { f_signal_send(F_signal_kill, process->child); - nanosleep(&wait, 0); + time.tv_sec = 0; + time.tv_nsec = controller_thread_exit_process_cancel_wait; + + nanosleep(&time, 0); } f_thread_join(process->id_thread, 0); diff --git a/level_3/controller/data/settings/example/entries/asynchronous-serial.entry b/level_3/controller/data/settings/example/entries/asynchronous-serial.entry new file mode 100644 index 0000000..d84da9b --- /dev/null +++ b/level_3/controller/data/settings/example/entries/asynchronous-serial.entry @@ -0,0 +1,12 @@ +# fss-0005 + +main: + + rule serial s_1 asynchronous + rule serial s_2 asynchronous + rule serial s_3 asynchronous + rule serial s_4 asynchronous + rule serial s_5 asynchronous + rule serial s_6 asynchronous + + ready diff --git a/level_3/controller/data/settings/example/entries/asynchronous.entry b/level_3/controller/data/settings/example/entries/asynchronous.entry index 5a2f516..3b189f8 100644 --- a/level_3/controller/data/settings/example/entries/asynchronous.entry +++ b/level_3/controller/data/settings/example/entries/asynchronous.entry @@ -1,8 +1,8 @@ # fss-0005 main: - consider asynchronous sleep_3 asynchronous - consider asynchronous sleep_5 asynchronous + consider asynchronous sleep_8 asynchronous + consider asynchronous sleep_10 asynchronous rule asynchronous sleep_1 asynchronous rule asynchronous sleep_2 asynchronous diff --git a/level_3/controller/data/settings/example/entries/serial.entry b/level_3/controller/data/settings/example/entries/serial.entry new file mode 100644 index 0000000..fcfcc82 --- /dev/null +++ b/level_3/controller/data/settings/example/entries/serial.entry @@ -0,0 +1,13 @@ +# fss-0005 + +main: + + consider serial s_1 + consider serial s_2 + consider serial s_3 + consider serial s_4 + consider serial s_5 + + rule serial s_6 + + ready diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule index 17671c1..e055c16 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule @@ -4,10 +4,13 @@ setting: name "Sleep 1 Seconds." nice 15 limit nice 1 2 - need asynchronous sleep_3 + need asynchronous sleep_10 + +script: + start echo "Sleeping 1: $(date -u)" script: start sleep 1 script: - start echo "Sleep 1: $(date -u)" + start echo "Slept 1: $(date -u)" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_10.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_10.rule index d4e89b0..63d644c 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_10.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_10.rule @@ -6,7 +6,10 @@ setting: limit nice 1 2 script: + start echo "Sleeping 10: $(date -u)" + +script: start sleep 10 script: - start echo "Sleep 10: $(date -u)" + start echo "Slept 10: $(date -u)" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule index a080fd1..77cbab5 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule @@ -4,10 +4,13 @@ setting: name "Sleep 2 Seconds." nice 15 limit nice 1 2 - need asynchronous sleep_5 + need asynchronous sleep_10 + +script: + start echo "Sleeping 2: $(date -u)" script: start sleep 2 script: - start echo "Sleep 2: $(date -u)" + start echo "Slept 2: $(date -u)" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule index 436fbbd..de91412 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule @@ -4,10 +4,13 @@ setting: name "Sleep 3 Seconds." nice 15 limit nice 1 2 - need asynchronous sleep_5 + need asynchronous sleep_8 + +script: + start echo "Sleeping 3: $(date -u)" script: start sleep 3 script: - start echo "Sleep 3: $(date -u)" + start echo "Slept 3: $(date -u)" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_5.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_5.rule index e6ede53..ecac2f5 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_5.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_5.rule @@ -6,7 +6,10 @@ setting: limit nice 1 2 script: + start echo "Sleeping 5: $(date -u)" + +script: start sleep 5 script: - start echo "Sleep 5: $(date -u)" + start echo "Slept 5: $(date -u)" diff --git a/level_3/controller/data/settings/example/rules/asynchronous/sleep_8.rule b/level_3/controller/data/settings/example/rules/asynchronous/sleep_8.rule index 1c5dbfc..34d919b 100644 --- a/level_3/controller/data/settings/example/rules/asynchronous/sleep_8.rule +++ b/level_3/controller/data/settings/example/rules/asynchronous/sleep_8.rule @@ -6,7 +6,10 @@ setting: limit nice 1 2 script: + start echo "Sleeping 8: $(date -u)" + +script: start sleep 8 script: - start echo "Sleep 8: $(date -u)" + start echo "Slept 8: $(date -u)" diff --git a/level_3/controller/data/settings/example/rules/serial/s_1.rule b/level_3/controller/data/settings/example/rules/serial/s_1.rule new file mode 100644 index 0000000..f434357 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/serial/s_1.rule @@ -0,0 +1,10 @@ +# fss-000d + +setting: + name "Serial 1" + +script: + start { + echo "Serial 1: $(date -u)" + sleep 1 + } diff --git a/level_3/controller/data/settings/example/rules/serial/s_2.rule b/level_3/controller/data/settings/example/rules/serial/s_2.rule new file mode 100644 index 0000000..1846b09 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/serial/s_2.rule @@ -0,0 +1,11 @@ +# fss-000d + +setting: + name "Serial 2" + need serial s_1 + +script: + start { + echo "Serial 2: $(date -u)" + sleep 1 + } diff --git a/level_3/controller/data/settings/example/rules/serial/s_3.rule b/level_3/controller/data/settings/example/rules/serial/s_3.rule new file mode 100644 index 0000000..9b0ef02 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/serial/s_3.rule @@ -0,0 +1,11 @@ +# fss-000d + +setting: + name "Serial 3" + need serial s_2 + +script: + start { + echo "Serial 3: $(date -u)" + sleep 1 + } diff --git a/level_3/controller/data/settings/example/rules/serial/s_4.rule b/level_3/controller/data/settings/example/rules/serial/s_4.rule new file mode 100644 index 0000000..46217ab --- /dev/null +++ b/level_3/controller/data/settings/example/rules/serial/s_4.rule @@ -0,0 +1,11 @@ +# fss-000d + +setting: + name "Serial 4" + need serial s_3 + +script: + start { + echo "Serial 4: $(date -u)" + sleep 1 + } diff --git a/level_3/controller/data/settings/example/rules/serial/s_5.rule b/level_3/controller/data/settings/example/rules/serial/s_5.rule new file mode 100644 index 0000000..f180252 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/serial/s_5.rule @@ -0,0 +1,11 @@ +# fss-000d + +setting: + name "Serial 5" + need serial s_4 + +script: + start { + echo "Serial 5: $(date -u)" + sleep 1 + } diff --git a/level_3/controller/data/settings/example/rules/serial/s_6.rule b/level_3/controller/data/settings/example/rules/serial/s_6.rule new file mode 100644 index 0000000..ec3b020 --- /dev/null +++ b/level_3/controller/data/settings/example/rules/serial/s_6.rule @@ -0,0 +1,11 @@ +# fss-000d + +setting: + name "Serial 6" + need serial s_5 + +script: + start { + echo "Serial 6: $(date -u)" + sleep 1 + }