On Tue, 28 Jun 2022 at 00:24, Yann Ylavic <ylavic....@gmail.com> wrote: > > Hi Ivan, > > On Mon, Jun 27, 2022 at 8:19 PM Ivan Zhakov <i...@visualsvn.com> wrote: > > > > For the longer version, let me first outline a few problems with the > > current apr_thread_current() API: > > > > 1) apr_thread_current_create() leaks the memory allocated in alloc_thread() > > after the thread exits. > > Hm, alloc_thread() creates an unmanaged pool (thread->pool) and > allocates everything for the thread on that pool (including the thread > itself). > This pool is destroyed when the thread is joined (for attached > threads), or for detached threads when apr_thread_exit() is called > explicitly otherwise when the thread function actually exits. > So which memory leaks precisely? apr_thread_current_create() for the > process' main thread may leak the thread pool on exit because it's > detached, it has not been apr_thread_create()d and is usually not > apr_thread_exit()ed, but when the main thread exits (hence the > process) nothing really leaks.. Should it matter we could also have an > explicit apr_thread_current_unleak(). > > Or do you mean the apr_threadattr_t that can be passed to > apr_thread_current_create()? That one is on the caller's > responsibility, and it can be reused for multiple threads if needed. > I meant a memory leak for native threads. For example, with an MPM that uses native Windows threadpool for worker threads.
> > > > 2) apr_thread_current_create() requires passing in the apr_threadattr_t > > value that may be unknown to the caller, so the result may not correspond > > to the actual thread attributes. > > This threadattr is for the users who know (attached vs detached > mainly), but if they don't know/care maybe they shouldn't use > apr_thread_current_create()? > It really matters if > apr_thread_detach()/apr_thread_join()/apr_thread_exit() is to be > called later anyway, but users that do this usually create their > thread with apr_thread_create(), and for these threads > apr_thread_current_create() is moot anyway (apr_thread_current() works > automagically for them). > The main usage of apr_thread_current_create() is for the main thread, > and this one is always detached per definition. But if users know the > nature/lifetime of the native threads they want to APR-ize, > apr_thread_current_create() can be useful too. > I think the problem is that in the general case the API user still needs to pass in the proper apr_threadattr_t to receive the proper current apr_thread_t object (even if there's a shortcut for some cases). > > > > 3) apr_thread_current() allows for situations where it can return NULL, > > such as with !APR_THREAD_LOCAL, making the API easy to misuse. > > Maybe we shouldn't define apr_thread_current{,_create}() when > !APR_THREAD_LOCAL? I find it useful though to determine if the current > thread is an APR one or not. > > > > > 4) apr_thread_current_after_fork() callback has to be called by the API > > user in the appropriate moments of time, allowing for a hard-to-spot API > > misuse. > > Again, this one is a helper for native threads, if apr_fork() is used > there is nothing to be done on the user side (httpd could do that in > the unix threaded MPMs for instance to avoid the calls to > apr_thread_current_after_fork()). > > > > > Since apr_thread_current() was added to implement the specific allocation > > scheme for PCRE2, > > I personally find the API qui useful per se, but admittedly I'm a bit > biased too.. > > > I think that we could try an alternative approach for that part of the > > problem. The alternative approach would have the same characteristics as > > the approach that had been used for PCRE1: > > > > -- Supply PCRE2 with a custom context with malloc and free implementations. > > -- Those implementations would work by either using a stack buffer for > > small allocations or by doing a malloc(). > > -- This allocation scheme would have the same properties as the existing > > scheme used when compiling with PCRE1. > > For PCRE2, you would malloc()/free() for every ap_regexec(), which can > be quite costly depending on the configuration/modules. This work was > done precisely for this concern, for the switch from PCRE1 to PCRE2 to > be as little costly as possible. The current implementation reuses the > same PCRE2 context per thread for the lifetime of httpd.. > Same for PCRE1, besides the stack buffer for small vectors (which is > still there currently btw). > I was thinking about having a custom malloc()/free() implementation that uses stack memory for first N bytes and then falls back to ordinary malloc/free(). So for cases where the allocation fits into the stack threshold there would be no malloc()/free() calls, as it was with PCRE1 and POSIX_MALLOC_THRESHOLD. I'll try to prepare a patch with this approach, to illustrate it better. > > > > With that in mind, the overall plan would be as follows: > > > > A) Implement the above approach for PCRE2 allocations in httpd. > > B) Remove the apr_thread_current() and APR_THREAD_LOCAL from APR trunk and > > 1.8.x. > > C) Release APR 1.8.0. > > Frankly, I find it hard to shoot down an API that works because it > could be misused when playing with native threads, whereas it works > transparently when using the APR threads directly. > > Isn't somewhere: > apr_thread_data_set(my_data, my_key, NULL, apr_thread_current()); > And elsewhere: > apr_thread_data_get(&my_data, my_key, apr_thread_current()); > quite nice compared to creating a native threadkey (whose number is > usually limited) for each entry? I don't find it too useful per-se: 1. The code has to rely that apr_thread_current() doesn't return NULL for some cases. 2. Changing data for other thread can lead to race conditions. What about detached threads? What if a thread already exited? 3. There is no guarantee that the chosen key doesn't conflict with other module/application. I think that the threadkey() API doesn't have these problems. And it should be faster, because it does not require a hash lookup.