misc: Add twalk_r function

Message ID 87ef5lezyp.fsf@oldenburg2.str.redhat.com
State New
Headers show
Series
  • misc: Add twalk_r function
Related show

Commit Message

Florian Weimer April 29, 2019, 12:51 p.m.
The twalk function is very difficult to use in a multi-threaded
program because there is no way to pass external state to the
iterator function.

(I expect to use this new function in sem_close and
__gconv_release_shlib, but the function is generally useful.)

2019-04-29  Florian Weimer  <fweimer@redhat.com>

	misc: Add twalk_r function.
	* include/search.h (__twalk_r): Declare.
	* manual/examples/twalk.c: New file.
	* manual/search.texi (Tree Search Function): Document twalk_r.
	* misc/Versions (2.30): Export twalk_r.
	(GLIBC_PRIVATE): Export __twalk_r.
	* misc/search.h [__USE_GNU] (twalk_r): Declare.
	* misc/tsearch.c (trecurse_r, __twalk_r): New functions.
	(twalk_r): Add weak alias.
	* misc/tst-tsearch.c (struct walk_trace_element): Define.
	(walk_trace): New variable.
	(struct twalk_with_twalk_r_closure): Define.
	(twalk_with_twalk_r_action): New function.
	(twalk_with_twalk_r): Likewise.
	(walk_action): Call walk_trace_add.
	(walk_tree_with): Rename from walk_tree.  Add walk argument.
	(walk_tree): New function.
	* sysdeps/mach/hurd/i386/libc.abilist (GLIBC_2.30): Add twalk_r.
	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):
	Likewise.

Comments

Carlos O'Donell April 30, 2019, 5:41 p.m. | #1
On 4/29/19 8:51 AM, Florian Weimer wrote:
> The twalk function is very difficult to use in a multi-threaded

> program because there is no way to pass external state to the

> iterator function.


I like *_r variants of existing functions. Thanks for adding this.

OK with typo fix in manual (see below s/threat/thread/g).

For a new API though I'd like to see one more reviewer, can you
track down someone else?

Reviewed-by: Carlos O'Donell <carlos@redhat.com>


> (I expect to use this new function in sem_close and

> __gconv_release_shlib, but the function is generally useful.)

> 

> 2019-04-29  Florian Weimer  <fweimer@redhat.com>

> 

> 	misc: Add twalk_r function.

> 	* include/search.h (__twalk_r): Declare.

> 	* manual/examples/twalk.c: New file.

> 	* manual/search.texi (Tree Search Function): Document twalk_r.

> 	* misc/Versions (2.30): Export twalk_r.

> 	(GLIBC_PRIVATE): Export __twalk_r.

> 	* misc/search.h [__USE_GNU] (twalk_r): Declare.

> 	* misc/tsearch.c (trecurse_r, __twalk_r): New functions.

> 	(twalk_r): Add weak alias.

> 	* misc/tst-tsearch.c (struct walk_trace_element): Define.

> 	(walk_trace): New variable.

> 	(struct twalk_with_twalk_r_closure): Define.

> 	(twalk_with_twalk_r_action): New function.

> 	(twalk_with_twalk_r): Likewise.

> 	(walk_action): Call walk_trace_add.

> 	(walk_tree_with): Rename from walk_tree.  Add walk argument.

> 	(walk_tree): New function.

> 	* sysdeps/mach/hurd/i386/libc.abilist (GLIBC_2.30): Add twalk_r.

> 	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 

> diff --git a/NEWS b/NEWS

> index 792ffb1ec8..a32bcbd7a4 100644

> --- a/NEWS

> +++ b/NEWS

> @@ -16,6 +16,8 @@ Major new features:

>   * The dynamic linker accepts the --preload argument to preload shared

>     objects, in addition to the LD_PRELOAD environment variable.

>   

> +* The twalk_r function has been added.


OK.

> +

>   * On Linux, the gettid function has been added.

>   

>   * Minguo (Republic of China) calendar support has been added as an

> diff --git a/include/search.h b/include/search.h

> index e17693022d..72fbc94476 100644

> --- a/include/search.h

> +++ b/include/search.h

> @@ -23,6 +23,8 @@ extern void *__tdelete (const void *__key, void **__rootp,

>   libc_hidden_proto (__tdelete)

>   extern void __twalk (const void *__root, __action_fn_t action);

>   libc_hidden_proto (__twalk)

> +extern __typeof__ (twalk_r) __twalk_r;

> +libc_hidden_proto (__twalk_r)


OK.

>   extern void __tdestroy (void *__root, __free_fn_t freefct);

>   libc_hidden_proto (__tdestroy)

>   #endif

> diff --git a/manual/examples/twalk.c b/manual/examples/twalk.c

> new file mode 100644

> index 0000000000..04e32731d6

> --- /dev/null

> +++ b/manual/examples/twalk.c

> @@ -0,0 +1,56 @@

> +/* Implement twalk using twalk_r.


OK.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU General Public License

> +   along with this program; if not, see <http://www.gnu.org/licenses/>.

> +*/

> +

> +#include <search.h>

> +

> +struct twalk_with_twalk_r_closure

> +{

> +  void (*action) (const void *, VISIT, int);

> +  int depth;

> +};


OK. Closure.

> +

> +static void

> +twalk_with_twalk_r_action (const void *nodep, VISIT which, void *closure0)

> +{

> +  struct twalk_with_twalk_r_closure *closure = closure0;

> +

> +  switch (which)

> +    {

> +    case leaf:

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    case preorder:

> +      closure->action (nodep, which, closure->depth);

> +      ++closure->depth;

> +      break;

> +    case postorder:

> +      /* The preorder action incremented the depth.  */

> +      closure->action (nodep, which, closure->depth - 1);

> +      break;

> +    case endorder:

> +      --closure->depth;

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    }

> +}


OK. Call a specific function, with depth, and take action. Good example.

> +

> +void

> +twalk (const void *root, void (*action) (const void *, VISIT, int))

> +{

> +  struct twalk_with_twalk_r_closure closure = { action, 0 };

> +  twalk_r (root, twalk_with_twalk_r_action, &closure);

> +}


OK.

> diff --git a/manual/search.texi b/manual/search.texi

> index 1574c96562..26f2ceeb55 100644

> --- a/manual/search.texi

> +++ b/manual/search.texi

> @@ -618,5 +618,28 @@ Since the functions used for the @var{action} parameter to @code{twalk}

>   must not modify the tree data, it is safe to run @code{twalk} in more

>   than one thread at the same time, working on the same tree.  It is also

>   safe to call @code{tfind} in parallel.  Functions which modify the tree

> -must not be used, otherwise the behavior is undefined.

> +must not be used, otherwise the behavior is undefined. However, it is

> +difficult to pass data external to the tree to the callback function

> +without resorting to global variables (and threat safety issues), so


s/threat/thread/g

> +see the @code{twalk_r} function below.

> +@end deftypefun

> +

> +@deftypefun void twalk_r (const void *@var{root}, void (*@var{action}) (const void *@var{key}, VISIT @var{which}, void *@var{closure}), void *@var{closure})

> +@standards{GNU, search.h}

> +@safety{@prelim{}@mtsafe{@mtsrace{:root}}@assafe{}@acsafe{}}

> +For each node in the tree with a node pointed to by @var{root}, the

> +@code{twalk_r} function calls the function provided by the parameter

> +@var{action}.  For leaf nodes the function is called exactly once with

> +@var{value} set to @code{leaf}.  For internal nodes the function is

> +called three times, setting the @var{value} parameter or @var{action} to

> +the appropriate value.  The @var{closure} parameter is passed down to

> +each call of the @var{action} function, unmodified.

> +


OK.

> +It is possible to implement the @code{twalk} function on top of the

> +@code{twalk_r} function, which is why there is no separate level

> +parameter.


OK.

> +

> +@smallexample

> +@include twalk.c.texi

> +@end smallexample

>   @end deftypefun

> diff --git a/misc/Versions b/misc/Versions

> index 900e4ffb79..e749582369 100644

> --- a/misc/Versions

> +++ b/misc/Versions

> @@ -158,11 +158,14 @@ libc {

>     GLIBC_2.26 {

>       preadv2; preadv64v2; pwritev2; pwritev64v2;

>     }

> +  GLIBC_2.30 {

> +    twalk_r;


OK.

> +  }

>     GLIBC_PRIVATE {

>       __madvise;

>       __mktemp;

>       __libc_ifunc_impl_list;

> -    __tdelete; __tfind; __tsearch; __twalk;

> +    __tdelete; __tfind; __tsearch; __twalk; __twalk_r;


OK.

>       __mmap; __munmap; __mprotect;

>       __sched_get_priority_min; __sched_get_priority_max;

>       __libc_allocate_once_slow;

> diff --git a/misc/search.h b/misc/search.h

> index 47e8a43436..be3cc25ef9 100644

> --- a/misc/search.h

> +++ b/misc/search.h

> @@ -150,6 +150,12 @@ typedef void (*__action_fn_t) (const void *__nodep, VISIT __value,

>   extern void twalk (const void *__root, __action_fn_t __action);

>   

>   #ifdef __USE_GNU

> +/* Like twalk, but pass down an additional closure parameter.  */

> +extern void twalk_r (const void *__root,

> +		     void (*) (const void *__nodep, VISIT __value,

> +			       void *__closure),

> +		     void *__closure);

> +


OK.

>   /* Callback type for function to free a tree node.  If the keys are atomic

>      data this function should do nothing.  */

>   typedef void (*__free_fn_t) (void *__nodep);

> diff --git a/misc/tsearch.c b/misc/tsearch.c

> index 5c19082a59..24d8d69de3 100644

> --- a/misc/tsearch.c

> +++ b/misc/tsearch.c

> @@ -719,7 +719,41 @@ __twalk (const void *vroot, __action_fn_t action)

>   libc_hidden_def (__twalk)

>   weak_alias (__twalk, twalk)

>   

> +/* twalk_r is the same as twalk, but with an additional closure

> +   parameter.  */

> +static void

> +trecurse_r (const void *vroot, void (*action) (const void *, VISIT, void *),

> +	    void *closure)

> +{

> +  const_node root = (const_node) vroot;

>   

> +  if (LEFT(root) == NULL && RIGHT(root) == NULL)

> +    (*action) (root, leaf, closure);

> +  else

> +    {

> +      (*action) (root, preorder, closure);

> +      if (LEFT(root) != NULL)

> +	trecurse_r (LEFT(root), action, closure);

> +      (*action) (root, postorder, closure);

> +      if (RIGHT(root) != NULL)

> +	trecurse_r (RIGHT(root), action, closure);

> +      (*action) (root, endorder, closure);

> +    }

> +}


OK. Pass closure to all calls.

> +

> +void

> +__twalk_r (const void *vroot, void (*action) (const void *, VISIT, void *),

> +	   void *closure)

> +{

> +  const_node root = (const_node) vroot;

> +

> +  CHECK_TREE ((node) root);

> +

> +  if (root != NULL && action != NULL)

> +    trecurse_r (root, action, closure);

> +}


OK.

> +libc_hidden_def (__twalk_r)

> +weak_alias (__twalk_r, twalk_r)

>   

>   /* The standardized functions miss an important functionality: the

>      tree cannot be removed easily.  We provide a function to do this.  */

> diff --git a/misc/tst-tsearch.c b/misc/tst-tsearch.c

> index 5803a456e0..9a570dd6c9 100644

> --- a/misc/tst-tsearch.c

> +++ b/misc/tst-tsearch.c

> @@ -25,6 +25,7 @@

>   #include <string.h>

>   #include <search.h>

>   #include <tst-stack-align.h>

> +#include <support/check.h>

>   

>   #define SEED 0

>   #define BALANCED 1

> @@ -74,6 +75,20 @@ static int max_depth;

>   

>   static int stack_align_check[2];

>   

> +/* Used to compare walk traces between the two implementations.  */

> +struct walk_trace_element

> +{

> +  const void *key;

> +  VISIT which;

> +  int depth;

> +};


OK.

> +#define DYNARRAY_STRUCT walk_trace_list

> +#define DYNARRAY_ELEMENT struct walk_trace_element

> +#define DYNARRAY_PREFIX walk_trace_

> +#define DYNARRAY_INITIAL_SIZE 0

> +#include <malloc/dynarray-skeleton.c>

> +static struct walk_trace_list walk_trace;

> +

>   /* Compare two keys.  */

>   static int

>   cmp_fn (const void *a, const void *b)

> @@ -102,11 +117,54 @@ memfry (int *string)

>       }

>   }

>   

> +struct twalk_with_twalk_r_closure

> +{

> +  void (*action) (const void *, VISIT, int);

> +  int depth;

> +};


OK.

> +

> +static void

> +twalk_with_twalk_r_action (const void *nodep, VISIT which, void *closure0)

> +{

> +  struct twalk_with_twalk_r_closure *closure = closure0;

> +

> +  switch (which)

> +    {

> +    case leaf:

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    case preorder:

> +      closure->action (nodep, which, closure->depth);

> +      ++closure->depth;

> +      break;

> +    case postorder:

> +      /* The preorder action incremented the depth.  */

> +      closure->action (nodep, which, closure->depth - 1);

> +      break;

> +    case endorder:

> +      --closure->depth;

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    }

> +}


OK.

> +

> +static void

> +twalk_with_twalk_r (const void *root,

> +		    void (*action) (const void *, VISIT, int))

> +{

> +  struct twalk_with_twalk_r_closure closure = { action, 0 };

> +  twalk_r (root, twalk_with_twalk_r_action, &closure);

> +  TEST_COMPARE (closure.depth, 0);

> +}


OK.

> +

>   static void

>   walk_action (const void *nodep, const VISIT which, const int depth)

>   {

>     int key = **(int **) nodep;

>   

> +  walk_trace_add (&walk_trace,

> +		  (struct walk_trace_element) { nodep, which, depth });

> +


OK.

>     if (!stack_align_check[1])

>       stack_align_check[1] = TEST_STACK_ALIGN () ? -1 : 1;

>   

> @@ -128,14 +186,16 @@ walk_action (const void *nodep, const VISIT which, const int depth)

>   }

>   

>   static void

> -walk_tree (void *root, int expected_count)

> +walk_tree_with (void *root, int expected_count,

> +		void (*walk) (const void *,

> +			      void (*) (const void *, VISIT, int)))


OK.

>   {

>     int i;

>   

>     memset (z, 0, sizeof z);

>     max_depth = 0;

>   

> -  twalk (root, walk_action);

> +  walk (root, walk_action);


OK.

>     for (i = 0; i < expected_count; ++i)

>       if (z[i] != 1)

>         {

> @@ -154,6 +214,31 @@ walk_tree (void *root, int expected_count)

>       }

>   }

>   

> +static void

> +walk_tree (void *root, int expected_count)

> +{

> +  walk_trace_clear (&walk_trace);

> +  walk_tree_with (root, expected_count, twalk);

> +  TEST_VERIFY (!walk_trace_has_failed (&walk_trace));

> +  size_t first_list_size;

> +  struct walk_trace_element *first_list

> +    = walk_trace_finalize (&walk_trace, &first_list_size);

> +

> +  walk_tree_with (root, expected_count, twalk_with_twalk_r);

> +

> +  /* Compare the two traces.  */

> +  TEST_COMPARE (first_list_size, walk_trace_size (&walk_trace));

> +  for (size_t i = 0; i < first_list_size && i < walk_trace_size (&walk_trace);

> +       ++i)

> +    {

> +      TEST_VERIFY (first_list[i].key == walk_trace_at (&walk_trace, i)->key);

> +      TEST_COMPARE (first_list[i].which, walk_trace_at (&walk_trace, i)->which);

> +      TEST_COMPARE (first_list[i].depth, walk_trace_at (&walk_trace, i)->depth);

> +    }

> +

> +  walk_trace_free (&walk_trace);

> +}


OK.

> +

>   /* Perform an operation on a tree.  */

>   static void

>   mangle_tree (enum order how, enum action what, void **root, int lag)

> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist

> index dd3cf6f9f2..1fc7ab2433 100644

> --- a/sysdeps/mach/hurd/i386/libc.abilist

> +++ b/sysdeps/mach/hurd/i386/libc.abilist

> @@ -2175,6 +2175,7 @@ GLIBC_2.3.4 setipv4sourcefilter F

>   GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist

> index f3b44d723f..b7283ad486 100644

> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist

> @@ -2142,3 +2142,4 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist

> index fd81fc4ad0..a817e683a4 100644

> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist

> @@ -2217,6 +2217,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist

> index f451fefac9..97b6cbf903 100644

> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist

> @@ -127,6 +127,7 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _Exit F

>   GLIBC_2.4 _IO_2_1_stderr_ D 0xa0

>   GLIBC_2.4 _IO_2_1_stdin_ D 0xa0

> diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist

> index 018d02b414..796e622a6d 100644

> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist

> @@ -2086,3 +2086,4 @@ GLIBC_2.29 xencrypt F

>   GLIBC_2.29 xprt_register F

>   GLIBC_2.29 xprt_unregister F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist

> index fc3c5d5c27..4e7d71d112 100644

> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist

> @@ -2038,6 +2038,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist

> index f2b04dbbff..11bd8c601a 100644

> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist

> @@ -2204,6 +2204,7 @@ GLIBC_2.3.4 vm86 F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist

> index 10ecf2e47c..589cce5b8d 100644

> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist

> @@ -2070,6 +2070,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist

> index 814e81b9d2..4b3727a91d 100644

> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist

> @@ -128,6 +128,7 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _Exit F

>   GLIBC_2.4 _IO_2_1_stderr_ D 0x98

>   GLIBC_2.4 _IO_2_1_stdin_ D 0x98

> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist

> index 68e80372e7..4be30c0339 100644

> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist

> @@ -2147,6 +2147,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist

> index 31178e4f54..e0010965a8 100644

> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist

> @@ -2134,3 +2134,4 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> index 7074573638..2a62f67741 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> @@ -2121,6 +2121,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> index 154f9c77fc..5a23f11fc9 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> @@ -2119,6 +2119,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> index 97b8f42d5c..32532a19da 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> @@ -2127,6 +2127,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> index 5b3e85de93..882f5cb5bf 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> @@ -2121,6 +2121,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist

> index 04a130a81c..c99a299bab 100644

> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist

> @@ -2175,3 +2175,4 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> index a701584422..d5f4364197 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> @@ -2177,6 +2177,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> index bbb647cd98..581fd10290 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> @@ -2210,6 +2210,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> index bb23bf61a8..ce10e0436e 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> @@ -2040,6 +2040,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> index 7921dda979..1cc729da7f 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> @@ -2244,3 +2244,4 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist

> index da123d3867..2bda5692f4 100644

> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist

> @@ -2104,3 +2104,4 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist

> index 2aed339af4..dddc138a1e 100644

> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist

> @@ -2172,6 +2172,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist

> index e46feb56e5..b2c75a48a8 100644

> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist

> @@ -2076,6 +2076,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist

> index 24a8f934cb..8b83449b16 100644

> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist

> @@ -2042,6 +2042,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist

> index ebdbd2c5ae..a237be9d93 100644

> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist

> @@ -2166,6 +2166,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>   GLIBC_2.30 __nldbl_warn F

>   GLIBC_2.30 __nldbl_warnx F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 _IO_fprintf F

>   GLIBC_2.4 _IO_printf F

>   GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist

> index 0992349b06..b850694434 100644

> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist

> @@ -2093,6 +2093,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist

> index af004fcff6..c6f22f0c7b 100644

> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist

> @@ -2051,6 +2051,7 @@ GLIBC_2.3.4 setsourcefilter F

>   GLIBC_2.3.4 xdr_quad_t F

>   GLIBC_2.3.4 xdr_u_quad_t F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>   GLIBC_2.4 __confstr_chk F

>   GLIBC_2.4 __fgets_chk F

>   GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist

> index 84015f0a57..1184385bce 100644

> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist

> @@ -2150,3 +2150,4 @@ GLIBC_2.29 getcpu F

>   GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>   GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>   GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> 


OK.

-- 
Cheers,
Carlos.
Florian Weimer April 30, 2019, 5:57 p.m. | #2
* Carlos O'Donell:

>> diff --git a/manual/search.texi b/manual/search.texi

>> index 1574c96562..26f2ceeb55 100644

>> --- a/manual/search.texi

>> +++ b/manual/search.texi

>> @@ -618,5 +618,28 @@ Since the functions used for the @var{action} parameter to @code{twalk}

>>   must not modify the tree data, it is safe to run @code{twalk} in more

>>   than one thread at the same time, working on the same tree.  It is also

>>   safe to call @code{tfind} in parallel.  Functions which modify the tree

>> -must not be used, otherwise the behavior is undefined.

>> +must not be used, otherwise the behavior is undefined. However, it is

>> +difficult to pass data external to the tree to the callback function

>> +without resorting to global variables (and threat safety issues), so

>

> s/threat/thread/g


Oops, thanks.  There's also a whitespace issue.  Fixed and pushed to the
fw/twalk_r branch.

diff --git a/manual/search.texi b/manual/search.texi
index 26f2ceeb55..979732f027 100644
--- a/manual/search.texi
+++ b/manual/search.texi
@@ -618,9 +618,9 @@ Since the functions used for the @var{action} parameter to @code{twalk}
 must not modify the tree data, it is safe to run @code{twalk} in more
 than one thread at the same time, working on the same tree.  It is also
 safe to call @code{tfind} in parallel.  Functions which modify the tree
-must not be used, otherwise the behavior is undefined. However, it is
+must not be used, otherwise the behavior is undefined.  However, it is
 difficult to pass data external to the tree to the callback function
-without resorting to global variables (and threat safety issues), so
+without resorting to global variables (and thread safety issues), so
 see the @code{twalk_r} function below.
 @end deftypefun

I will wait for a second review before pushing this to master.

Florian
Adhemerval Zanella April 30, 2019, 7:09 p.m. | #3
On 29/04/2019 09:51, Florian Weimer wrote:
> The twalk function is very difficult to use in a multi-threaded

> program because there is no way to pass external state to the

> iterator function.


LGTM as well, with some nits below (the whitespace you seems to 
fixed already).

> 

> (I expect to use this new function in sem_close and

> __gconv_release_shlib, but the function is generally useful.)


As a side note I worked on a sem_open/sem_close refactor some time ago
that uses dynarray and the char array derived structure, aimed to remove
both the twalk and alloca internal usage. It turned to be a slight simpler
implementation as well and I will try to send it upstream.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>


> 

> 2019-04-29  Florian Weimer  <fweimer@redhat.com>

> 

> 	misc: Add twalk_r function.

> 	* include/search.h (__twalk_r): Declare.

> 	* manual/examples/twalk.c: New file.

> 	* manual/search.texi (Tree Search Function): Document twalk_r.

> 	* misc/Versions (2.30): Export twalk_r.

> 	(GLIBC_PRIVATE): Export __twalk_r.

> 	* misc/search.h [__USE_GNU] (twalk_r): Declare.

> 	* misc/tsearch.c (trecurse_r, __twalk_r): New functions.

> 	(twalk_r): Add weak alias.

> 	* misc/tst-tsearch.c (struct walk_trace_element): Define.

> 	(walk_trace): New variable.

> 	(struct twalk_with_twalk_r_closure): Define.

> 	(twalk_with_twalk_r_action): New function.

> 	(twalk_with_twalk_r): Likewise.

> 	(walk_action): Call walk_trace_add.

> 	(walk_tree_with): Rename from walk_tree.  Add walk argument.

> 	(walk_tree): New function.

> 	* sysdeps/mach/hurd/i386/libc.abilist (GLIBC_2.30): Add twalk_r.

> 	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> 	(GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.

> 	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):

> 	Likewise.

> 

> diff --git a/NEWS b/NEWS

> index 792ffb1ec8..a32bcbd7a4 100644

> --- a/NEWS

> +++ b/NEWS

> @@ -16,6 +16,8 @@ Major new features:

>  * The dynamic linker accepts the --preload argument to preload shared

>    objects, in addition to the LD_PRELOAD environment variable.

>  

> +* The twalk_r function has been added.

> +


I think we can extend it description a bit by adding it is a GNU extension
that behaves like twalk but with an addional parameter to be passed in 
callback action function (like qsort_r).

>  * On Linux, the gettid function has been added.

>  

>  * Minguo (Republic of China) calendar support has been added as an

> diff --git a/include/search.h b/include/search.h

> index e17693022d..72fbc94476 100644

> --- a/include/search.h

> +++ b/include/search.h

> @@ -23,6 +23,8 @@ extern void *__tdelete (const void *__key, void **__rootp,

>  libc_hidden_proto (__tdelete)

>  extern void __twalk (const void *__root, __action_fn_t action);

>  libc_hidden_proto (__twalk)

> +extern __typeof__ (twalk_r) __twalk_r;

> +libc_hidden_proto (__twalk_r)

>  extern void __tdestroy (void *__root, __free_fn_t freefct);

>  libc_hidden_proto (__tdestroy)

>  #endif


Ok.

> diff --git a/manual/examples/twalk.c b/manual/examples/twalk.c

> new file mode 100644

> index 0000000000..04e32731d6

> --- /dev/null

> +++ b/manual/examples/twalk.c

> @@ -0,0 +1,56 @@

> +/* Implement twalk using twalk_r.

> +   Copyright (C) 2019 Free Software Foundation, Inc.

> +

> +   The GNU C Library is free software; you can redistribute it and/or

> +   modify it under the terms of the GNU Lesser General Public

> +   License as published by the Free Software Foundation; either

> +   version 2.1 of the License, or (at your option) any later version.

> +

> +   The GNU C Library is distributed in the hope that it will be useful,

> +   but WITHOUT ANY WARRANTY; without even the implied warranty of

> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> +   Lesser General Public License for more details.

> +

> +   You should have received a copy of the GNU General Public License

> +   along with this program; if not, see <http://www.gnu.org/licenses/>.

> +*/

> +

> +#include <search.h>

> +

> +struct twalk_with_twalk_r_closure

> +{

> +  void (*action) (const void *, VISIT, int);

> +  int depth;

> +};

> +

> +static void

> +twalk_with_twalk_r_action (const void *nodep, VISIT which, void *closure0)

> +{

> +  struct twalk_with_twalk_r_closure *closure = closure0;

> +

> +  switch (which)

> +    {

> +    case leaf:

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    case preorder:

> +      closure->action (nodep, which, closure->depth);

> +      ++closure->depth;

> +      break;

> +    case postorder:

> +      /* The preorder action incremented the depth.  */

> +      closure->action (nodep, which, closure->depth - 1);

> +      break;

> +    case endorder:

> +      --closure->depth;

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    }

> +}

> +

> +void

> +twalk (const void *root, void (*action) (const void *, VISIT, int))

> +{

> +  struct twalk_with_twalk_r_closure closure = { action, 0 };

> +  twalk_r (root, twalk_with_twalk_r_action, &closure);

> +}


Ok.

> diff --git a/manual/search.texi b/manual/search.texi

> index 1574c96562..26f2ceeb55 100644

> --- a/manual/search.texi

> +++ b/manual/search.texi

> @@ -618,5 +618,28 @@ Since the functions used for the @var{action} parameter to @code{twalk}

>  must not modify the tree data, it is safe to run @code{twalk} in more

>  than one thread at the same time, working on the same tree.  It is also

>  safe to call @code{tfind} in parallel.  Functions which modify the tree

> -must not be used, otherwise the behavior is undefined.

> +must not be used, otherwise the behavior is undefined. However, it is


I think it is missing two spaces after period.

> +difficult to pass data external to the tree to the callback function

> +without resorting to global variables (and threat safety issues), so

> +see the @code{twalk_r} function below.

> +@end deftypefun

> +

> +@deftypefun void twalk_r (const void *@var{root}, void (*@var{action}) (const void *@var{key}, VISIT @var{which}, void *@var{closure}), void *@var{closure})

> +@standards{GNU, search.h}

> +@safety{@prelim{}@mtsafe{@mtsrace{:root}}@assafe{}@acsafe{}}

> +For each node in the tree with a node pointed to by @var{root}, the

> +@code{twalk_r} function calls the function provided by the parameter

> +@var{action}.  For leaf nodes the function is called exactly once with

> +@var{value} set to @code{leaf}.  For internal nodes the function is

> +called three times, setting the @var{value} parameter or @var{action} to

> +the appropriate value.  The @var{closure} parameter is passed down to

> +each call of the @var{action} function, unmodified.

> +

> +It is possible to implement the @code{twalk} function on top of the

> +@code{twalk_r} function, which is why there is no separate level

> +parameter.

> +

> +@smallexample

> +@include twalk.c.texi

> +@end smallexample

>  @end deftypefun


Ok.

> diff --git a/misc/Versions b/misc/Versions

> index 900e4ffb79..e749582369 100644

> --- a/misc/Versions

> +++ b/misc/Versions

> @@ -158,11 +158,14 @@ libc {

>    GLIBC_2.26 {

>      preadv2; preadv64v2; pwritev2; pwritev64v2;

>    }

> +  GLIBC_2.30 {

> +    twalk_r;

> +  }

>    GLIBC_PRIVATE {

>      __madvise;

>      __mktemp;

>      __libc_ifunc_impl_list;

> -    __tdelete; __tfind; __tsearch; __twalk;

> +    __tdelete; __tfind; __tsearch; __twalk; __twalk_r;

>      __mmap; __munmap; __mprotect;

>      __sched_get_priority_min; __sched_get_priority_max;

>      __libc_allocate_once_slow;


Ok.

> diff --git a/misc/search.h b/misc/search.h

> index 47e8a43436..be3cc25ef9 100644

> --- a/misc/search.h

> +++ b/misc/search.h

> @@ -150,6 +150,12 @@ typedef void (*__action_fn_t) (const void *__nodep, VISIT __value,

>  extern void twalk (const void *__root, __action_fn_t __action);

>  

>  #ifdef __USE_GNU

> +/* Like twalk, but pass down an additional closure parameter.  */

> +extern void twalk_r (const void *__root,

> +		     void (*) (const void *__nodep, VISIT __value,

> +			       void *__closure),

> +		     void *__closure);

> +

>  /* Callback type for function to free a tree node.  If the keys are atomic

>     data this function should do nothing.  */

>  typedef void (*__free_fn_t) (void *__nodep);


Ok.

> diff --git a/misc/tsearch.c b/misc/tsearch.c

> index 5c19082a59..24d8d69de3 100644

> --- a/misc/tsearch.c

> +++ b/misc/tsearch.c

> @@ -719,7 +719,41 @@ __twalk (const void *vroot, __action_fn_t action)

>  libc_hidden_def (__twalk)

>  weak_alias (__twalk, twalk)

>  

> +/* twalk_r is the same as twalk, but with an additional closure

> +   parameter.  */

> +static void

> +trecurse_r (const void *vroot, void (*action) (const void *, VISIT, void *),

> +	    void *closure)

> +{

> +  const_node root = (const_node) vroot;

>  

> +  if (LEFT(root) == NULL && RIGHT(root) == NULL)

> +    (*action) (root, leaf, closure);

> +  else

> +    {

> +      (*action) (root, preorder, closure);

> +      if (LEFT(root) != NULL)

> +	trecurse_r (LEFT(root), action, closure);

> +      (*action) (root, postorder, closure);

> +      if (RIGHT(root) != NULL)

> +	trecurse_r (RIGHT(root), action, closure);

> +      (*action) (root, endorder, closure);

> +    }

> +}

> +

> +void

> +__twalk_r (const void *vroot, void (*action) (const void *, VISIT, void *),

> +	   void *closure)

> +{

> +  const_node root = (const_node) vroot;

> +

> +  CHECK_TREE ((node) root);

> +

> +  if (root != NULL && action != NULL)

> +    trecurse_r (root, action, closure);

> +}

> +libc_hidden_def (__twalk_r)

> +weak_alias (__twalk_r, twalk_r)

>  

>  /* The standardized functions miss an important functionality: the

>     tree cannot be removed easily.  We provide a function to do this.  */


Ok.

> diff --git a/misc/tst-tsearch.c b/misc/tst-tsearch.c

> index 5803a456e0..9a570dd6c9 100644

> --- a/misc/tst-tsearch.c

> +++ b/misc/tst-tsearch.c

> @@ -25,6 +25,7 @@

>  #include <string.h>

>  #include <search.h>

>  #include <tst-stack-align.h>

> +#include <support/check.h>

>  

>  #define SEED 0

>  #define BALANCED 1

> @@ -74,6 +75,20 @@ static int max_depth;

>  

>  static int stack_align_check[2];

>  

> +/* Used to compare walk traces between the two implementations.  */

> +struct walk_trace_element

> +{

> +  const void *key;

> +  VISIT which;

> +  int depth;

> +};

> +#define DYNARRAY_STRUCT walk_trace_list

> +#define DYNARRAY_ELEMENT struct walk_trace_element

> +#define DYNARRAY_PREFIX walk_trace_

> +#define DYNARRAY_INITIAL_SIZE 0

> +#include <malloc/dynarray-skeleton.c>

> +static struct walk_trace_list walk_trace;

> +

>  /* Compare two keys.  */

>  static int

>  cmp_fn (const void *a, const void *b)

> @@ -102,11 +117,54 @@ memfry (int *string)

>      }

>  }

>  

> +struct twalk_with_twalk_r_closure

> +{

> +  void (*action) (const void *, VISIT, int);

> +  int depth;

> +};

> +

> +static void

> +twalk_with_twalk_r_action (const void *nodep, VISIT which, void *closure0)

> +{

> +  struct twalk_with_twalk_r_closure *closure = closure0;

> +

> +  switch (which)

> +    {

> +    case leaf:

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    case preorder:

> +      closure->action (nodep, which, closure->depth);

> +      ++closure->depth;

> +      break;

> +    case postorder:

> +      /* The preorder action incremented the depth.  */

> +      closure->action (nodep, which, closure->depth - 1);

> +      break;

> +    case endorder:

> +      --closure->depth;

> +      closure->action (nodep, which, closure->depth);

> +      break;

> +    }

> +}

> +

> +static void

> +twalk_with_twalk_r (const void *root,

> +		    void (*action) (const void *, VISIT, int))

> +{

> +  struct twalk_with_twalk_r_closure closure = { action, 0 };

> +  twalk_r (root, twalk_with_twalk_r_action, &closure);

> +  TEST_COMPARE (closure.depth, 0);

> +}

> +


Ok.

>  static void

>  walk_action (const void *nodep, const VISIT which, const int depth)

>  {

>    int key = **(int **) nodep;

>  

> +  walk_trace_add (&walk_trace,

> +		  (struct walk_trace_element) { nodep, which, depth });

> +

>    if (!stack_align_check[1])

>      stack_align_check[1] = TEST_STACK_ALIGN () ? -1 : 1;


Ok.

>  

> @@ -128,14 +186,16 @@ walk_action (const void *nodep, const VISIT which, const int depth)

>  }

>  

>  static void

> -walk_tree (void *root, int expected_count)

> +walk_tree_with (void *root, int expected_count,

> +		void (*walk) (const void *,

> +			      void (*) (const void *, VISIT, int)))

>  {

>    int i;

>  

>    memset (z, 0, sizeof z);

>    max_depth = 0;

>  

> -  twalk (root, walk_action);

> +  walk (root, walk_action);

>    for (i = 0; i < expected_count; ++i)

>      if (z[i] != 1)

>        {

> @@ -154,6 +214,31 @@ walk_tree (void *root, int expected_count)

>      }

>  }

>  


Ok.

> +static void

> +walk_tree (void *root, int expected_count)

> +{

> +  walk_trace_clear (&walk_trace);

> +  walk_tree_with (root, expected_count, twalk);

> +  TEST_VERIFY (!walk_trace_has_failed (&walk_trace));

> +  size_t first_list_size;

> +  struct walk_trace_element *first_list

> +    = walk_trace_finalize (&walk_trace, &first_list_size);

> +

> +  walk_tree_with (root, expected_count, twalk_with_twalk_r);

> +

> +  /* Compare the two traces.  */

> +  TEST_COMPARE (first_list_size, walk_trace_size (&walk_trace));

> +  for (size_t i = 0; i < first_list_size && i < walk_trace_size (&walk_trace);

> +       ++i)

> +    {

> +      TEST_VERIFY (first_list[i].key == walk_trace_at (&walk_trace, i)->key);

> +      TEST_COMPARE (first_list[i].which, walk_trace_at (&walk_trace, i)->which);

> +      TEST_COMPARE (first_list[i].depth, walk_trace_at (&walk_trace, i)->depth);

> +    }

> +

> +  walk_trace_free (&walk_trace);

> +}

> +

>  /* Perform an operation on a tree.  */

>  static void

>  mangle_tree (enum order how, enum action what, void **root, int lag)


Ok.

> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist

> index dd3cf6f9f2..1fc7ab2433 100644

> --- a/sysdeps/mach/hurd/i386/libc.abilist

> +++ b/sysdeps/mach/hurd/i386/libc.abilist

> @@ -2175,6 +2175,7 @@ GLIBC_2.3.4 setipv4sourcefilter F

>  GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist

> index f3b44d723f..b7283ad486 100644

> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist

> @@ -2142,3 +2142,4 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist

> index fd81fc4ad0..a817e683a4 100644

> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist

> @@ -2217,6 +2217,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist

> index f451fefac9..97b6cbf903 100644

> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist

> @@ -127,6 +127,7 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _Exit F

>  GLIBC_2.4 _IO_2_1_stderr_ D 0xa0

>  GLIBC_2.4 _IO_2_1_stdin_ D 0xa0

> diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist

> index 018d02b414..796e622a6d 100644

> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist

> @@ -2086,3 +2086,4 @@ GLIBC_2.29 xencrypt F

>  GLIBC_2.29 xprt_register F

>  GLIBC_2.29 xprt_unregister F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist

> index fc3c5d5c27..4e7d71d112 100644

> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist

> @@ -2038,6 +2038,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist

> index f2b04dbbff..11bd8c601a 100644

> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist

> @@ -2204,6 +2204,7 @@ GLIBC_2.3.4 vm86 F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist

> index 10ecf2e47c..589cce5b8d 100644

> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist

> @@ -2070,6 +2070,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist

> index 814e81b9d2..4b3727a91d 100644

> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist

> @@ -128,6 +128,7 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _Exit F

>  GLIBC_2.4 _IO_2_1_stderr_ D 0x98

>  GLIBC_2.4 _IO_2_1_stdin_ D 0x98

> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist

> index 68e80372e7..4be30c0339 100644

> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist

> @@ -2147,6 +2147,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist

> index 31178e4f54..e0010965a8 100644

> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist

> @@ -2134,3 +2134,4 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> index 7074573638..2a62f67741 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist

> @@ -2121,6 +2121,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> index 154f9c77fc..5a23f11fc9 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist

> @@ -2119,6 +2119,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> index 97b8f42d5c..32532a19da 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist

> @@ -2127,6 +2127,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> index 5b3e85de93..882f5cb5bf 100644

> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist

> @@ -2121,6 +2121,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist

> index 04a130a81c..c99a299bab 100644

> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist

> @@ -2175,3 +2175,4 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> index a701584422..d5f4364197 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist

> @@ -2177,6 +2177,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> index bbb647cd98..581fd10290 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist

> @@ -2210,6 +2210,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> index bb23bf61a8..ce10e0436e 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist

> @@ -2040,6 +2040,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> index 7921dda979..1cc729da7f 100644

> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist

> @@ -2244,3 +2244,4 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist

> index da123d3867..2bda5692f4 100644

> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist

> @@ -2104,3 +2104,4 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist

> index 2aed339af4..dddc138a1e 100644

> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist

> @@ -2172,6 +2172,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist

> index e46feb56e5..b2c75a48a8 100644

> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist

> @@ -2076,6 +2076,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist

> index 24a8f934cb..8b83449b16 100644

> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist

> @@ -2042,6 +2042,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist

> index ebdbd2c5ae..a237be9d93 100644

> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist

> @@ -2166,6 +2166,7 @@ GLIBC_2.30 __nldbl_vwarnx F

>  GLIBC_2.30 __nldbl_warn F

>  GLIBC_2.30 __nldbl_warnx F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 _IO_fprintf F

>  GLIBC_2.4 _IO_printf F

>  GLIBC_2.4 _IO_sprintf F

> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist

> index 0992349b06..b850694434 100644

> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist

> @@ -2093,6 +2093,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist

> index af004fcff6..c6f22f0c7b 100644

> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist

> @@ -2051,6 +2051,7 @@ GLIBC_2.3.4 setsourcefilter F

>  GLIBC_2.3.4 xdr_quad_t F

>  GLIBC_2.3.4 xdr_u_quad_t F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

>  GLIBC_2.4 __confstr_chk F

>  GLIBC_2.4 __fgets_chk F

>  GLIBC_2.4 __fgets_unlocked_chk F

> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist

> index 84015f0a57..1184385bce 100644

> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist

> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist

> @@ -2150,3 +2150,4 @@ GLIBC_2.29 getcpu F

>  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F

>  GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F

>  GLIBC_2.30 gettid F

> +GLIBC_2.30 twalk_r F

> 


Ok.
Florian Weimer May 1, 2019, 6:11 a.m. | #4
* Adhemerval Zanella:

> On 29/04/2019 09:51, Florian Weimer wrote:

>> The twalk function is very difficult to use in a multi-threaded

>> program because there is no way to pass external state to the

>> iterator function.

>

> LGTM as well, with some nits below (the whitespace you seems to 

> fixed already).

>

>> 

>> (I expect to use this new function in sem_close and

>> __gconv_release_shlib, but the function is generally useful.)

>

> As a side note I worked on a sem_open/sem_close refactor some time ago

> that uses dynarray and the char array derived structure, aimed to remove

> both the twalk and alloca internal usage. It turned to be a slight simpler

> implementation as well and I will try to send it upstream.

>

> Reviewed-by: Carlos O'Donell <carlos@redhat.com>


I assume this should be your Reviewed-by?

>> diff --git a/NEWS b/NEWS

>> index 792ffb1ec8..a32bcbd7a4 100644

>> --- a/NEWS

>> +++ b/NEWS

>> @@ -16,6 +16,8 @@ Major new features:

>>  * The dynamic linker accepts the --preload argument to preload shared

>>    objects, in addition to the LD_PRELOAD environment variable.

>>  

>> +* The twalk_r function has been added.

>> +

>

> I think we can extend it description a bit by adding it is a GNU extension

> that behaves like twalk but with an addional parameter to be passed in 

> callback action function (like qsort_r).


“The twalk_r function has been added.  It is similar to the existing
twalk function, but it passes an additional caller-supplied argument to
the callback function.”

Thanks,
Florian
Adhemerval Zanella May 1, 2019, 10:56 a.m. | #5
> Il giorno 1 mag 2019, alle ore 03:11, Florian Weimer <fweimer@redhat.com> ha scritto:

> 

> * Adhemerval Zanella:

> 

>>> On 29/04/2019 09:51, Florian Weimer wrote:

>>> The twalk function is very difficult to use in a multi-threaded

>>> program because there is no way to pass external state to the

>>> iterator function.

>> 

>> LGTM as well, with some nits below (the whitespace you seems to 

>> fixed already).

>> 

>>> 

>>> (I expect to use this new function in sem_close and

>>> __gconv_release_shlib, but the function is generally useful.)

>> 

>> As a side note I worked on a sem_open/sem_close refactor some time ago

>> that uses dynarray and the char array derived structure, aimed to remove

>> both the twalk and alloca internal usage. It turned to be a slight simpler

>> implementation as well and I will try to send it upstream.

>> 

>> Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> 

> I assume this should be your Reviewed-by?


Oops, indeed.

> 

>>> diff --git a/NEWS b/NEWS

>>> index 792ffb1ec8..a32bcbd7a4 100644

>>> --- a/NEWS

>>> +++ b/NEWS

>>> @@ -16,6 +16,8 @@ Major new features:

>>> * The dynamic linker accepts the --preload argument to preload shared

>>>   objects, in addition to the LD_PRELOAD environment variable.

>>> 

>>> +* The twalk_r function has been added.

>>> +

>> 

>> I think we can extend it description a bit by adding it is a GNU extension

>> that behaves like twalk but with an addional parameter to be passed in 

>> callback action function (like qsort_r).

> 

> “The twalk_r function has been added.  It is similar to the existing

> twalk function, but it passes an additional caller-supplied argument to

> the callback function.”

> 


LGTM.

> Thanks,

> Florian
Joseph Myers May 2, 2019, 4:43 p.m. | #6
I'm seeing (at least with GCC 8):

tst-tsearch.c: In function 'walk_tree.constprop':
tst-tsearch.c:231:3: error: 'first_list_size' may be used uninitialized in this function [-Werror=maybe-uninitialized]
   for (size_t i = 0; i < first_list_size && i < walk_trace_size (&walk_trace);
   ^~~

https://sourceware.org/ml/libc-testresults/2019-q2/msg00121.html

-- 
Joseph S. Myers
joseph@codesourcery.com
Florian Weimer May 2, 2019, 5:10 p.m. | #7
* Joseph Myers:

> I'm seeing (at least with GCC 8):

>

> tst-tsearch.c: In function 'walk_tree.constprop':

> tst-tsearch.c:231:3: error: 'first_list_size' may be used uninitialized in this function [-Werror=maybe-uninitialized]

>    for (size_t i = 0; i < first_list_size && i < walk_trace_size (&walk_trace);

>    ^~~


Huh.  Sorry about that.  I don't get this error, with a GCC 8.3-based
compiler.

It's arguably a test bug.  Does this patch fix it for you?

diff --git a/misc/tst-tsearch.c b/misc/tst-tsearch.c
index 9a570dd6c9..07d13622b1 100644
--- a/misc/tst-tsearch.c
+++ b/misc/tst-tsearch.c
@@ -223,6 +223,7 @@ walk_tree (void *root, int expected_count)
   size_t first_list_size;
   struct walk_trace_element *first_list
     = walk_trace_finalize (&walk_trace, &first_list_size);
+  TEST_VERIFY_EXIT (first_list != NULL);
 
   walk_tree_with (root, expected_count, twalk_with_twalk_r);
 
It assumes that the tree is not empty, but I think this is true in this
test.

Thanks,
Florian
Joseph Myers May 2, 2019, 8 p.m. | #8
On Thu, 2 May 2019, Florian Weimer wrote:

> It's arguably a test bug.  Does this patch fix it for you?


Yes, this fixes the testsuite build.

-- 
Joseph S. Myers
joseph@codesourcery.com
Florian Weimer May 3, 2019, 9:05 a.m. | #9
* Joseph Myers:

> On Thu, 2 May 2019, Florian Weimer wrote:

>

>> It's arguably a test bug.  Does this patch fix it for you?

>

> Yes, this fixes the testsuite build.


Thanks, this is what I pushed to master.

Florian

misc/tst-tsearch: Additional explicit error checking

This avoids an undefined variable warning with certain GCC versions.

2019-05-03  Florian Weimer  <fweimer@redhat.com>

	* misc/tst-tsearch.c (walk_tree): Add more error checking.

diff --git a/misc/tst-tsearch.c b/misc/tst-tsearch.c
index 9a570dd6c9..44895d4c0a 100644
--- a/misc/tst-tsearch.c
+++ b/misc/tst-tsearch.c
@@ -223,8 +223,10 @@ walk_tree (void *root, int expected_count)
   size_t first_list_size;
   struct walk_trace_element *first_list
     = walk_trace_finalize (&walk_trace, &first_list_size);
+  TEST_VERIFY_EXIT (first_list != NULL);
 
   walk_tree_with (root, expected_count, twalk_with_twalk_r);
+  TEST_VERIFY (!walk_trace_has_failed (&walk_trace));
 
   /* Compare the two traces.  */
   TEST_COMPARE (first_list_size, walk_trace_size (&walk_trace));

Patch

diff --git a/NEWS b/NEWS
index 792ffb1ec8..a32bcbd7a4 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,8 @@  Major new features:
 * The dynamic linker accepts the --preload argument to preload shared
   objects, in addition to the LD_PRELOAD environment variable.
 
+* The twalk_r function has been added.
+
 * On Linux, the gettid function has been added.
 
 * Minguo (Republic of China) calendar support has been added as an
diff --git a/include/search.h b/include/search.h
index e17693022d..72fbc94476 100644
--- a/include/search.h
+++ b/include/search.h
@@ -23,6 +23,8 @@  extern void *__tdelete (const void *__key, void **__rootp,
 libc_hidden_proto (__tdelete)
 extern void __twalk (const void *__root, __action_fn_t action);
 libc_hidden_proto (__twalk)
+extern __typeof__ (twalk_r) __twalk_r;
+libc_hidden_proto (__twalk_r)
 extern void __tdestroy (void *__root, __free_fn_t freefct);
 libc_hidden_proto (__tdestroy)
 #endif
diff --git a/manual/examples/twalk.c b/manual/examples/twalk.c
new file mode 100644
index 0000000000..04e32731d6
--- /dev/null
+++ b/manual/examples/twalk.c
@@ -0,0 +1,56 @@ 
+/* Implement twalk using twalk_r.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <search.h>
+
+struct twalk_with_twalk_r_closure
+{
+  void (*action) (const void *, VISIT, int);
+  int depth;
+};
+
+static void
+twalk_with_twalk_r_action (const void *nodep, VISIT which, void *closure0)
+{
+  struct twalk_with_twalk_r_closure *closure = closure0;
+
+  switch (which)
+    {
+    case leaf:
+      closure->action (nodep, which, closure->depth);
+      break;
+    case preorder:
+      closure->action (nodep, which, closure->depth);
+      ++closure->depth;
+      break;
+    case postorder:
+      /* The preorder action incremented the depth.  */
+      closure->action (nodep, which, closure->depth - 1);
+      break;
+    case endorder:
+      --closure->depth;
+      closure->action (nodep, which, closure->depth);
+      break;
+    }
+}
+
+void
+twalk (const void *root, void (*action) (const void *, VISIT, int))
+{
+  struct twalk_with_twalk_r_closure closure = { action, 0 };
+  twalk_r (root, twalk_with_twalk_r_action, &closure);
+}
diff --git a/manual/search.texi b/manual/search.texi
index 1574c96562..26f2ceeb55 100644
--- a/manual/search.texi
+++ b/manual/search.texi
@@ -618,5 +618,28 @@  Since the functions used for the @var{action} parameter to @code{twalk}
 must not modify the tree data, it is safe to run @code{twalk} in more
 than one thread at the same time, working on the same tree.  It is also
 safe to call @code{tfind} in parallel.  Functions which modify the tree
-must not be used, otherwise the behavior is undefined.
+must not be used, otherwise the behavior is undefined. However, it is
+difficult to pass data external to the tree to the callback function
+without resorting to global variables (and threat safety issues), so
+see the @code{twalk_r} function below.
+@end deftypefun
+
+@deftypefun void twalk_r (const void *@var{root}, void (*@var{action}) (const void *@var{key}, VISIT @var{which}, void *@var{closure}), void *@var{closure})
+@standards{GNU, search.h}
+@safety{@prelim{}@mtsafe{@mtsrace{:root}}@assafe{}@acsafe{}}
+For each node in the tree with a node pointed to by @var{root}, the
+@code{twalk_r} function calls the function provided by the parameter
+@var{action}.  For leaf nodes the function is called exactly once with
+@var{value} set to @code{leaf}.  For internal nodes the function is
+called three times, setting the @var{value} parameter or @var{action} to
+the appropriate value.  The @var{closure} parameter is passed down to
+each call of the @var{action} function, unmodified.
+
+It is possible to implement the @code{twalk} function on top of the
+@code{twalk_r} function, which is why there is no separate level
+parameter.
+
+@smallexample
+@include twalk.c.texi
+@end smallexample
 @end deftypefun
diff --git a/misc/Versions b/misc/Versions
index 900e4ffb79..e749582369 100644
--- a/misc/Versions
+++ b/misc/Versions
@@ -158,11 +158,14 @@  libc {
   GLIBC_2.26 {
     preadv2; preadv64v2; pwritev2; pwritev64v2;
   }
+  GLIBC_2.30 {
+    twalk_r;
+  }
   GLIBC_PRIVATE {
     __madvise;
     __mktemp;
     __libc_ifunc_impl_list;
-    __tdelete; __tfind; __tsearch; __twalk;
+    __tdelete; __tfind; __tsearch; __twalk; __twalk_r;
     __mmap; __munmap; __mprotect;
     __sched_get_priority_min; __sched_get_priority_max;
     __libc_allocate_once_slow;
diff --git a/misc/search.h b/misc/search.h
index 47e8a43436..be3cc25ef9 100644
--- a/misc/search.h
+++ b/misc/search.h
@@ -150,6 +150,12 @@  typedef void (*__action_fn_t) (const void *__nodep, VISIT __value,
 extern void twalk (const void *__root, __action_fn_t __action);
 
 #ifdef __USE_GNU
+/* Like twalk, but pass down an additional closure parameter.  */
+extern void twalk_r (const void *__root,
+		     void (*) (const void *__nodep, VISIT __value,
+			       void *__closure),
+		     void *__closure);
+
 /* Callback type for function to free a tree node.  If the keys are atomic
    data this function should do nothing.  */
 typedef void (*__free_fn_t) (void *__nodep);
diff --git a/misc/tsearch.c b/misc/tsearch.c
index 5c19082a59..24d8d69de3 100644
--- a/misc/tsearch.c
+++ b/misc/tsearch.c
@@ -719,7 +719,41 @@  __twalk (const void *vroot, __action_fn_t action)
 libc_hidden_def (__twalk)
 weak_alias (__twalk, twalk)
 
+/* twalk_r is the same as twalk, but with an additional closure
+   parameter.  */
+static void
+trecurse_r (const void *vroot, void (*action) (const void *, VISIT, void *),
+	    void *closure)
+{
+  const_node root = (const_node) vroot;
 
+  if (LEFT(root) == NULL && RIGHT(root) == NULL)
+    (*action) (root, leaf, closure);
+  else
+    {
+      (*action) (root, preorder, closure);
+      if (LEFT(root) != NULL)
+	trecurse_r (LEFT(root), action, closure);
+      (*action) (root, postorder, closure);
+      if (RIGHT(root) != NULL)
+	trecurse_r (RIGHT(root), action, closure);
+      (*action) (root, endorder, closure);
+    }
+}
+
+void
+__twalk_r (const void *vroot, void (*action) (const void *, VISIT, void *),
+	   void *closure)
+{
+  const_node root = (const_node) vroot;
+
+  CHECK_TREE ((node) root);
+
+  if (root != NULL && action != NULL)
+    trecurse_r (root, action, closure);
+}
+libc_hidden_def (__twalk_r)
+weak_alias (__twalk_r, twalk_r)
 
 /* The standardized functions miss an important functionality: the
    tree cannot be removed easily.  We provide a function to do this.  */
diff --git a/misc/tst-tsearch.c b/misc/tst-tsearch.c
index 5803a456e0..9a570dd6c9 100644
--- a/misc/tst-tsearch.c
+++ b/misc/tst-tsearch.c
@@ -25,6 +25,7 @@ 
 #include <string.h>
 #include <search.h>
 #include <tst-stack-align.h>
+#include <support/check.h>
 
 #define SEED 0
 #define BALANCED 1
@@ -74,6 +75,20 @@  static int max_depth;
 
 static int stack_align_check[2];
 
+/* Used to compare walk traces between the two implementations.  */
+struct walk_trace_element
+{
+  const void *key;
+  VISIT which;
+  int depth;
+};
+#define DYNARRAY_STRUCT walk_trace_list
+#define DYNARRAY_ELEMENT struct walk_trace_element
+#define DYNARRAY_PREFIX walk_trace_
+#define DYNARRAY_INITIAL_SIZE 0
+#include <malloc/dynarray-skeleton.c>
+static struct walk_trace_list walk_trace;
+
 /* Compare two keys.  */
 static int
 cmp_fn (const void *a, const void *b)
@@ -102,11 +117,54 @@  memfry (int *string)
     }
 }
 
+struct twalk_with_twalk_r_closure
+{
+  void (*action) (const void *, VISIT, int);
+  int depth;
+};
+
+static void
+twalk_with_twalk_r_action (const void *nodep, VISIT which, void *closure0)
+{
+  struct twalk_with_twalk_r_closure *closure = closure0;
+
+  switch (which)
+    {
+    case leaf:
+      closure->action (nodep, which, closure->depth);
+      break;
+    case preorder:
+      closure->action (nodep, which, closure->depth);
+      ++closure->depth;
+      break;
+    case postorder:
+      /* The preorder action incremented the depth.  */
+      closure->action (nodep, which, closure->depth - 1);
+      break;
+    case endorder:
+      --closure->depth;
+      closure->action (nodep, which, closure->depth);
+      break;
+    }
+}
+
+static void
+twalk_with_twalk_r (const void *root,
+		    void (*action) (const void *, VISIT, int))
+{
+  struct twalk_with_twalk_r_closure closure = { action, 0 };
+  twalk_r (root, twalk_with_twalk_r_action, &closure);
+  TEST_COMPARE (closure.depth, 0);
+}
+
 static void
 walk_action (const void *nodep, const VISIT which, const int depth)
 {
   int key = **(int **) nodep;
 
+  walk_trace_add (&walk_trace,
+		  (struct walk_trace_element) { nodep, which, depth });
+
   if (!stack_align_check[1])
     stack_align_check[1] = TEST_STACK_ALIGN () ? -1 : 1;
 
@@ -128,14 +186,16 @@  walk_action (const void *nodep, const VISIT which, const int depth)
 }
 
 static void
-walk_tree (void *root, int expected_count)
+walk_tree_with (void *root, int expected_count,
+		void (*walk) (const void *,
+			      void (*) (const void *, VISIT, int)))
 {
   int i;
 
   memset (z, 0, sizeof z);
   max_depth = 0;
 
-  twalk (root, walk_action);
+  walk (root, walk_action);
   for (i = 0; i < expected_count; ++i)
     if (z[i] != 1)
       {
@@ -154,6 +214,31 @@  walk_tree (void *root, int expected_count)
     }
 }
 
+static void
+walk_tree (void *root, int expected_count)
+{
+  walk_trace_clear (&walk_trace);
+  walk_tree_with (root, expected_count, twalk);
+  TEST_VERIFY (!walk_trace_has_failed (&walk_trace));
+  size_t first_list_size;
+  struct walk_trace_element *first_list
+    = walk_trace_finalize (&walk_trace, &first_list_size);
+
+  walk_tree_with (root, expected_count, twalk_with_twalk_r);
+
+  /* Compare the two traces.  */
+  TEST_COMPARE (first_list_size, walk_trace_size (&walk_trace));
+  for (size_t i = 0; i < first_list_size && i < walk_trace_size (&walk_trace);
+       ++i)
+    {
+      TEST_VERIFY (first_list[i].key == walk_trace_at (&walk_trace, i)->key);
+      TEST_COMPARE (first_list[i].which, walk_trace_at (&walk_trace, i)->which);
+      TEST_COMPARE (first_list[i].depth, walk_trace_at (&walk_trace, i)->depth);
+    }
+
+  walk_trace_free (&walk_trace);
+}
+
 /* Perform an operation on a tree.  */
 static void
 mangle_tree (enum order how, enum action what, void **root, int lag)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index dd3cf6f9f2..1fc7ab2433 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2175,6 +2175,7 @@  GLIBC_2.3.4 setipv4sourcefilter F
 GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index f3b44d723f..b7283ad486 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2142,3 +2142,4 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fd81fc4ad0..a817e683a4 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2217,6 +2217,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index f451fefac9..97b6cbf903 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -127,6 +127,7 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 018d02b414..796e622a6d 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2086,3 +2086,4 @@  GLIBC_2.29 xencrypt F
 GLIBC_2.29 xprt_register F
 GLIBC_2.29 xprt_unregister F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index fc3c5d5c27..4e7d71d112 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2038,6 +2038,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index f2b04dbbff..11bd8c601a 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2204,6 +2204,7 @@  GLIBC_2.3.4 vm86 F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 10ecf2e47c..589cce5b8d 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2070,6 +2070,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 814e81b9d2..4b3727a91d 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -128,6 +128,7 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 68e80372e7..4be30c0339 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2147,6 +2147,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 31178e4f54..e0010965a8 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2134,3 +2134,4 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 7074573638..2a62f67741 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2121,6 +2121,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 154f9c77fc..5a23f11fc9 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2119,6 +2119,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 97b8f42d5c..32532a19da 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2127,6 +2127,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 5b3e85de93..882f5cb5bf 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2121,6 +2121,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 04a130a81c..c99a299bab 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2175,3 +2175,4 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index a701584422..d5f4364197 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2177,6 +2177,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index bbb647cd98..581fd10290 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2210,6 +2210,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index bb23bf61a8..ce10e0436e 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2040,6 +2040,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 7921dda979..1cc729da7f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2244,3 +2244,4 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index da123d3867..2bda5692f4 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2104,3 +2104,4 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 2aed339af4..dddc138a1e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2172,6 +2172,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index e46feb56e5..b2c75a48a8 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2076,6 +2076,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 24a8f934cb..8b83449b16 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2042,6 +2042,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index ebdbd2c5ae..a237be9d93 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2166,6 +2166,7 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 0992349b06..b850694434 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2093,6 +2093,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index af004fcff6..c6f22f0c7b 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2051,6 +2051,7 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 84015f0a57..1184385bce 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2150,3 +2150,4 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 twalk_r F