path: root/thread.c
AgeCommit message (Collapse)Author
2018-11-07Add cast to suppress warnings on Solaris [ci skip]nobu
git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-11-06Revert "thread.c (rb_wait_for_single_fd): no point initializing pollfd.revents"mame
It may cause an access to uninitialized variables. The call to ppoll will set the `revents` field, but ppoll is not always called because it is in the guard `!RUBY_VM_INTERRUPTED(th->ec)`. This issue was found by Coverity Scan. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-11-05thread.c (rb_wait_for_single_fd): no point initializing pollfd.reventsnormal
poll(2) and ppoll(2) implementations need to check and write to .revents on the initial scan, anyways. So any poll/ppoll call which returns a positive result can be expected to have an initialized .revents value. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-30support theap for T_HASH. [Feature #14989]ko1
* hash.c, internal.h: support theap for small Hash. Introduce RHASH_ARRAY (li_table) besides st_table and small Hash (<=8 entries) are managed by an array data structure. This array data can be managed by theap. If st_table is needed, then converting array data to st_table data. For st_table using code, we prepare "stlike" APIs which accepts hash value and are very similar to st_ APIs. This work is based on the GSoC achievement by tacinight <> and refined by ko1. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-30* expand tabs.svn
git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-30use RARRAY_AREF() instead of RARRAY_CONST_PTR().ko1
git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-20Remove tracecoverage instructionsmame
The instructions were used only for branch coverage. Instead, it now uses a trace framework [Feature #14104]. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-20add disabling MJIT features option.ko1
* introduce new configure option `--enable-mjit` and `--disable-mjit`. Default is "enable". `--disable-mjit` disables all of MJIT features so that `ruby --jit` can't enable MJIT. This option affect a macro `USE_MJIT`. This change remove `--enable/disable-install-mjit-header` option. * introduce the `ENABLE_MJIT` variable. * use `ENABLE_MJIT` option. * internal.h: respect `USE_MJIT`. Same as other *.c, *.h. * test/ruby/test_jit.rb: check `ENABLE_MJIT` key of rbconfg.rb. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-20thread.c (rb_clear_coverages): defined out of #ifdefmame
It failed to build on windows. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-10-20ext/coverage/: add the oneshot modemame
This patch introduces "oneshot_lines" mode for `Coverage.start`, which checks "whether each line was executed at least once or not", instead of "how many times each line was executed". A hook for each line is fired at most once, and after it is fired, the hook flag was removed; it runs with zero overhead. See [Feature #15022] in detail. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-09-14PRI_64_PREFIX "u" is called PRIu64shyouhei
See also ISO9899:1999 section 7.8.1 paragraph 3. The #ifndef is for C compilers older than C99. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-09-13thread.c: fix -Wformat warning where uint64_t is not unsigned longnobu
git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-09-13thread.c: fix -Wformat warningk0kubun
../thread.c:1219:18: warning: format specifies type 'unsigned long' but the argument has type 'rb_hrtime_t' (aka 'unsigned long long') [-Wformat] end, now); ^~~ ../thread.c:1219:23: warning: format specifies type 'unsigned long' but the argument has type 'rb_hrtime_t' (aka 'unsigned long long') [-Wformat] end, now); ^~~ git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-09-13thread.c (rb_thread_atfork): reinitialize current th->interrupt_locknormal
Another thread may be holding th->interrupt_lock while our current thread calls fork. Therefore we must reinitialize our own th->interrupt_lock in the child process because the owner of the lock is only in the parent. The original parent process is unaffected. We cannot destroy the lock while it has an unknown state, either, so some implementations can leak a small amount of memory, here (NPTL won't). git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-09-09thread.c: get rid of false warnings by gcc-8nobu
git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-29cont.c: set th->root_fiber to current fiber at forknormal
Otherwise, th->root_fiber can point to an invalid Fiber, because Fibers do not live across fork. So consider whatever Fiber is running the root fiber. [ruby-core:88723] [Bug #15041] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-27process.c: simplify SIGCHLD-based waitpidnormal
Introduce a new rb_thread_sleep_interruptible that does not execute interrupts before sleeping. Skipping the interrupt check before sleep is required for out-of-GVL ruby_waitpid_all to function properly when setting waitpid_state.ret Now that ubf_select can be called by the gvl.timer thread without recursive locking gvl.lock, we can safely use rb_threadptr_interrupt to deal with waking up sleeping processes, git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-27thread.c: check interrupts explicitly in select/ppoll blocking regionsnormal
The logic around blocking_region_begin is confusing to me, but the goal of this patch is to ensure rb_sigwait_fd_get and rb_sigwait_fd_put are matched. In other words, we don't want a thread to hold sigwait_fd forever if an exception is raised while calling select() or ppoll(). git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-25thread.c: quiet down -Wmaybe-uninitialized on gcc 7.[2-3]normal
Haven't tested gcc 8, yet; but gcc 6 seems fine.... git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-25hrtime.h: add documentationnormal
I updated the patch with documentation but forgot about it, earlier :x [ruby-core:88616] [Misc #15014] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-25thread.c: use rb_hrtime_t scalar for high-resolution time operationsnormal
Relying on "struct timespec" was too annoying API-wise and used more stack space. "double" was a bit wacky w.r.t rounding in the past, so now we'll switch to using a 64-bit type. Unsigned 64-bit integer is able to give us over nearly 585 years of range with nanoseconds. This range is good enough for the Linux kernel internal time representation, so it ought to be good enough for us. This reduces the stack usage of functions while GVL is held (and thus subject to marking) on x86-64 Linux (with ppoll): rb_wait_for_single_fd 120 => 104 do_select 120 => 88 [ruby-core:88582] [Misc #15014] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-24thread_pthread.c: use eventfd instead of pipe on Linuxnormal
Based on r64478, any regular user creating more than 1024 pipes on Linux will end up with tiny pipes with only a single page capacity. So avoid wasting user resources and use lighter eventfd on Linux. [ruby-core:88563] [Misc #15011] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-22compile.c: remove tracecoverage instruction for line coveragemame
Line coverage was based on special instruction "tracecoverage". Now, instead, it uses the mechanism of trace hook [Feature #14104]. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-22thread.c (rb_reset_coverages): remove coverage counters from all ISeqsmame
When coverage measurement is enabled, the compiler makes each iseq have a reference to the counter array of coverage. Even after coverage measurement is disabled, the reference is kept. And, if coverage measurement is restarted, a coverage hook will increase the counter. This is completely meaningless; it brings just overhead. To remove this meaninglessness, this change removes all the reference when coverage measuement is stopped. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-21thread*.c: replace GetMutexPtr with mutex_ptrnormal
Following ko1's lead in r59192, this gets rid of non-obvious assignments which happen inside macros. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-20thread*.c: avoid unnecessary initialization for list_for_each_safenormal
According to r52446, it is only necessary for the current item (@i), not the `@nxt` parameter for list_for_each_safe. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-20thread_pthread.c: reinitialize ubf_list at forknormal
It's possible for the ubf_list_head to be populated with dead threads at fork or the ubf_list_lock to be held, so reinitialize both at startup. And while we're at it, use a static initializer at startup to save a library call and kill some ifdef. [ruby-core:88578] [Bug #15013] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-19thread.c (rb_thread_fd_select): fix off-by-one with sigwait_fdnormal
select(2) needs the nfds argument to be one higher than the largest FD in the sets :x git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-19Revert "thread_sync.c (rb_mutex_sleep): skip interrupt check before sleep"normal
This reverts commit 2e420b8b99db4a5b81e2deda1ca386d59ad6bcba (r64464) git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-18thread_sync.c (rb_mutex_sleep): skip interrupt check before sleepnormal
We do not want to risk switching threads before going to sleep because it can cause unexpected wakeups and put us in an unexpected state when used with ConditionVariable. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-18Revert "thread.c (sleep_*): check interrupt before changing th->status"normal
This reverts commit 9e59487a38d914275bedcde723923f22b3779e59 (r64449) More (but different) CI failures I can't reproduce locally... git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-18thread.c (sleep_*): check interrupt before changing th->statusnormal
Having threads switch before we sleep can cause applications to misread the state of the thread. Now, we are consistent with blocking_region_begin behavior and change th->status AFTER checking interrupts. Maybe this can fix [Bug #15002] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-18thread.c (sleep_*): reduce the effect of spurious interruptsnormal
Spurious interrupts from SIGCHLD cause Mutex#sleep (via ConditionVariable#wait) to return early and breaks some use cases. Since these are outside the programs's control with MJIT, we will only consider pending interrupts (e.g. those from Thread#run) and signals which cause a Ruby-level Signal.trap handler to fire as "spurious" wakeups. [ruby-core:88537] [Feature #15002] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-16thread.c (consume_communication_pipe): disarm UBF_TIMER before consumenormal
Same reasoning as the disarm in rb_sigwait_fd_get, the current thread is already processing signals, so we do not need UBF_TIMER to continually kick the process, anymore. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-15vm_core.h (rb_thread_t): pack small fields togethernormal
On a 64-bit system, this reduces rb_thread_t from 536 to 520 bytes. Depending on the allocation, this can reduce cacheline access for checking the abort_on_exception, report_on_exception and pending_interrupt_queue_checked flags. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-13thread_pthread.c: eliminate timer thread by restructuring GVLnormal
This reverts commit 194a6a2c68e9c8a3536b24db18ceac87535a6051 (r64203). Race conditions which caused the original reversion will be fixed in the subsequent commit. [ruby-core:88360] [Misc #14937] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-06thread_pthread.c: restore timer-thread for now :<normal
[ruby-core:88306] Revert "process.c: ensure th->interrupt lock is held when migrating" This reverts commit 5ca416bdf6b6785cb20f139c2c514eda005fe42f (r64201) Revert "process.c (rb_waitpid): reduce sigwait_fd bouncing" This reverts commit 217bdd776fbeea3bfd0b9324eefbfcec3b1ccb3e (r64200). Revert "test/ruby/test_thread.rb (test_thread_timer_and_interrupt): add timeouts" This reverts commit 9f395f11202fc3c7edbd76f5aa6ce1f8a1e752a9 (r64199). Revert "thread_pthread.c (native_sleep): reduce ppoll sleeps" This reverts commit b3aa256c4d43d3d7e9975ec18eb127f45f623c9b (r64193). Revert "thread.c (consume_communication_pipe): do not retry after short read" This reverts commit 291a82f748de56e65fac10edefc51ec7a54a82d4 (r64185). Revert "test/ruby/test_io.rb (test_race_gets_and_close): timeout each thread" This reverts commit 3dbd8d1f66537f968f0461ed8547460b3b1241b3 (r64184). Revert "thread_pthread.c (gvl_acquire_common): persist timeout across calls" This reverts commit 8c2ae6e3ed072b06fc3cbc34fa8a14b2acbb49d5 (r64165). Revert "test/ruby/test_io.rb (test_race_gets_and_close): use SIGABRT on timeout" This reverts commit 931cda4db8afd6b544a8d85a6815765a9c417213 (r64135). Revert "thread_pthread.c (gvl_yield): do ubf wakeups when uncontended" This reverts commit 508f00314f46c08b6e9b0141c01355d24954260c (r64133). Revert "thread_pthread.h (native_thread_data): split condvars on some platforms" This reverts commit a038bf238bd9a24bf1e1622f618a27db261fc91b (r64124). Revert "process.c (waitpid_nogvl): prevent conflicting use of sleep_cond" This reverts commit 7018acc946882f21d519af7c42ccf84b22a46b27 (r64117). Revert "thread_pthread.c (rb_sigwait_sleep): th may be 0 from MJIT" This reverts commit 56491afc7916fb24f5c4dc2c632fb93fa7063992 (r64116). Revert "thread*.c: waiting on sigwait_fd performs periodic ubf wakeups" This reverts commit ab47a57a46e70634d049e4da20a5441c7a14cdec (r64115). Revert "thread_pthread.c (gvl_destroy): make no-op on GVL bits" This reverts commit 95cae748171f4754b97f4ba54da2ae62a8d484fd (r64114). Revert "thread_pthread.c (rb_sigwait_sleep): fix uninitialized poll set in UBF case" This reverts commit 4514362948fdb914c6138b12d961d92e9c0fee6c (r64113). Revert "thread_pthread.c (rb_sigwait_sleep): re-fix [Bug #5343] harder" This reverts commit 26b8a70bb309c7a367b9134045508b5b5a580a77 (r64111). Revert "thread.c: move ppoll wrapper into thread_pthread.c" This reverts commit 3dc7727d22fecbc355597edda25d2a245bf55ba1 (r64110). Revert "thread.c: move ppoll wrapper before thread_pthread.c" This reverts commit 2fa1e2e3c3c5c4b3ce84730dee4bcbe9d81b8e35 (r64109). Revert "thread_pthread.c (ubf_select): refix [Bug #5343]" This reverts commit 4c1ab82f0623eca91a95d2a44053be22bbce48ad (r64108). Revert "thread_win32.c: suppress warnings by -Wsuggest-attribute" This reverts commit 6a9b63e39075c53870933fbac5c1065f7d22047c (r64159). Revert "thread_pthread: remove timer-thread by restructuring GVL" This reverts commit 708bfd21156828526fe72de2cedecfaca6647dc1 (r64107). git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-08-04thread.c (consume_communication_pipe): do not retry after short readnormal
Saves a syscall and slightly improves vm_thread_condvar1 benchmark slightly (more improvements on the way): r64170 this patch vm_thread_condvar1 0.917 1.065 git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-30thread*.c: waiting on sigwait_fd performs periodic ubf wakeupsnormal
We need to be able to perform periodic ubf_list wakeups when a thread is sleeping and waiting on signals. [ruby-core:88088] [Misc #14937] [Bug #5343] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-30thread_pthread.c (rb_sigwait_sleep): re-fix [Bug #5343] hardernormal
We can't always designate a timer thread, so any sleepers must also perform ubf wakeups. Note: a similar change needs to be made for rb_thread_fd_select and rb_wait_for_single_fd. [ruby-core:88088] [Misc #14937] [Bug #5343] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-30thread.c: move ppoll wrapper into thread_pthread.cnormal
thread_pthread.c relies on ppoll for rb_sigwait_sleep, so ensure the compatibility wrapper is available for it. [Bug #14950] Reported-by: SHIBATA Hiroshi <> Reported-by: Greg L <> git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-29thread.c: move ppoll wrapper before thread_pthread.cnormal
thread_pthread.c relies on ppoll for rb_sigwait_sleep, so ensure the compatibility wrapper is available for it. Reported-by: SHIBATA Hiroshi <> git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-29thread_pthread: remove timer-thread by restructuring GVLnormal
To reduce resource use and reduce CI failure; remove timer-thread. Single-threaded Ruby processes (including forked children) will never see extra thread overhead. This prevents glibc and jemalloc from going into multi-threaded mode and initializing locks or causing fragmentation via arena explosion. The GVL is implements its own wait-queue as a ccan/list to permit controlling wakeup order. Timeslice under contention is handled by a designated timer thread (similar to choosing a "patrol_thread" for current deadlock checking). There is only one self-pipe, now, as wakeups for timeslice are done independently using condition variables. This reduces FD pressure slightly. Signal handling is handled directly by a Ruby Thread (instead of timer-thread) by exposing signal self-pipe to callers of rb_thread_fd_select, native_sleep, rb_wait_for_single_fd, etc... Acquiring, using, and releasing the self-pipe is exposed via 4 new internal functions: 1) rb_sigwait_fd_get - exclusively acquire timer_thread_pipe.normal[0] 2) rb_sigwait_fd_sleep - sleep and wait for signal (and no other FDs) 3) rb_sigwait_fd_put - release acquired result from rb_sigwait_fd_get 4) rb_sigwait_fd_migrate - migrate signal handling to another thread after calling rb_sigwait_fd_put. rb_sigwait_fd_migrate is necessary for waitpid callers because only one thread can wait on self-pipe at a time, otherwise a deadlock will occur if threads fight over the self-pipe. TRAP_INTERRUPT_MASK is now set for the main thread directly in signal handler via rb_thread_wakeup_timer_thread. Originally, I wanted to use POSIX timers (timer_create/timer_settime) for this. Unfortunately, this proved unfeasible as Mutex#sleep resumes on spurious wakeups and test/thread/test_cv.rb::test_condvar_timed_wait failed. Using pthread_sigmask to mask out SIGVTALRM fixed that test, but test/fiddle/test_function.rb::test_nogvl_poll proved there'd be some unavoidable (and frequent) incompatibilities from that approach. Finally, this allows us to drop thread_destruct_lock and interrupt current ec directly. We don't need to rely on vm->thread_destruct_lock or a coherent vm->running_thread on any platform. Separate timer-thread for time slice and signal handling is relegated to thread_win32.c, now. [ruby-core:88088] [Misc #14937] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-28thread.c (blocking_region_end): clear ubf before unregister_ubf_listnormal
If we keep ubf set after unregistering, there is a window for other threads (including timer thread) to put this thread back on the ubf_list right away. Entering ubf_list unexpectedly after GVL acquisition may cause spurious wakeup and trigger unexpected behavior. Finally, clear ubf before acquiring GVL, to since ubf is useless during GVL acquisition anyways and we don't want to waste cycles in other threads calling ubf for useless work. [ruby-core:88141] [Bug #14945] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-23Revert "thread.c (rb_thread_fd_select): remove unnecessary rb_fd_resize calls"normal
This reverts commit r64017 (git commit 2ff85621690f4f5b0dff9226ac68418a950a6919). Nevermind, haven't had enough coffee, yet :x git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-23thread.c (rb_thread_fd_select): remove unnecessary rb_fd_resize callsnormal
There's no need to resize each rb_fdset_t to match the size of the biggest one. This can allow some small memory savings if watching several sets of FDs simultaneously. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-21thread.c (do_select): fix leak on exceptionnormal
When do_select is interrupted and raise happens from RUBY_VM_CHECK_INTS_BLOCKING, the original FD sets we copied do not get freed, leading to a memory leak. Wrap up all the FD sets into a Ruby object to ensure the GC can release an allocations made for rb_fdset_t. This leak existed since Ruby 2.0.0 (r36430) [Bug #14929] git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-08mjit: get rid of memory leak in pause+resume loopnormal
pthread_atfork is not idempotent and repeatedly calling it causes it to register the same hook repeatedly; leading to unbound memory growth. Ruby already has a (confusing-named) internal API for to call in the forked child process: rb_thread_atfork Call the MJIT child_after_fork hook inside that to prevent unbound growth with the following loop: loop do RubyVM::MJIT.pause RubyVM::MJIT.resume end git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-06thread.c (ruby_ppoll): improve timespec to msec conversionnormal
Round up non-zero <1ms timeouts to 1ms and use INT_MAX instead of infinite (-1) for extremely large timeouts. All of our ppoll/select callers are able to handle spurious wakeups, anyways. This avoids excessive CPU usage and busy waits with short timeouts to rb_wait_for_single_fd. CPU usage with the following script is significantly reduced for systems with "#undef HAVE_PPOLL": require 'io/wait' r, w = IO.pipe { loop { r.wait_readable(0.000001) } }.join(5) exit!(0) Low-resolution in poll(2) still sucks, though... Note: I don't see the value in making a similar change to time_timeval of a <1us sleep is attempted because of GVL release and syscall latency. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-07-06thread.c: our fake ppoll implementation is staticnormal
Rename it to "ruby_ppoll" so it looks more obvious in debuggers. git-svn-id: svn+ssh:// b2dd03c8-39d4-4d8f-98ff-823fe69b080e