Hi Aaron, On Thu, 2025-05-22 at 18:28 -0400, Aaron Merey wrote: > Add new internal static library libthread.a that provides infrastructure > for eu-* tools to run functions concurrently using pthreads. > > threadlib.c manages per-job threads as well as per-job buffers for stdout > output. Output for each job is printed to stdout in the order that the > jobs were added to the job queue. This helps preserve the order of > output when parallelization is added to an eu-* tool. > > threadlib.h declares functions add_job and run_jobs. Jobs are added to > a threadlib.c internal job queue using add_job. run_jobs concurrently > executes jobs in parallel.
It isn't stated anywhere explicitly but I think this means all jobs must be added first with add_job and then when run_jobs will be called no new jobs can be added (concurrently)? I assumed this while reviewing, because if add_job can be called after run_jobs has already started by head would explode :} Could you document this and/or maybe even add an assert somewhere? > eu-readelf now links against libthread.a when elfutils is configured > with --enable-thread-safety. > > * src/Makefile.am: libthread.a is compiled and and linked with > readelf when USE_LOCKS is defined. > * src/threadlib.c: New file. Manages job creation, concurrent > execution and output handling. > * src/threadlib.h: New file. Declares functions add_job and > run_jobs. > > Signed-off-by: Aaron Merey <ame...@redhat.com> > > --- > v2: > Remove unnecessary thread_LDADD variable in src/Makefile.am. Minor comment > rewordings. > > src/Makefile.am | 16 +++ > src/threadlib.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++ > src/threadlib.h | 34 +++++++ > 3 files changed, 302 insertions(+) > create mode 100644 src/threadlib.c > create mode 100644 src/threadlib.h > > diff --git a/src/Makefile.am b/src/Makefile.am > index 4a3fb957..90f2d55f 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -52,6 +52,19 @@ libar.manifest: $(libar_a_OBJECTS) > MOSTLYCLEANFILES = *.gconv > CLEANFILES = $(bin_SCRIPTS) $(EXTRA_libar_a_DEPENDENCIES) > > +if USE_LOCKS > +noinst_LIBRARIES += libthread.a > + > +libthread_a_SOURCES = threadlib.c > + > +EXTRA_DIST += threadlib.h I think this should be be added to noinst_HEADERS which isn't defined yet in this Makefile.am, so it probably should be: noinst_HEADERS = threadlib.h Please double check with make distcheck and/or make rpmbuild > +libthread.manifest: $(libthread_a_OBJECTS) > + $(AM_V_GEN)echo $^ > $@ > + > +CLEANFILES += $(EXTRA_libthread_a_DEPENDENCIES) > +endif EXTRA_libthread_a_DEPENDENCIES doesn't seem to be defined anywhere, should it be defined first as: EXTRA_libtreahd_a_DEPENDENCIES = libthread.manifest ? > + > if BUILD_STATIC > libasm = ../libasm/libasm.a > libdw = ../libdw/libdw.a -lz $(zip_LIBS) $(libelf) -ldl -lpthread > @@ -91,6 +104,9 @@ ar_no_Wstack_usage = yes > unstrip_no_Wstack_usage = yes > > readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD) > +if USE_LOCKS > +readelf_LDADD += libthread.a > +endif > nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD) > $(obstack_LIBS) \ > $(demanglelib) > size_LDADD = $(libelf) $(libeu) $(argp_LDADD) OK. > diff --git a/src/threadlib.c b/src/threadlib.c > new file mode 100644 > index 00000000..ab158b69 > --- /dev/null > +++ b/src/threadlib.c > @@ -0,0 +1,252 @@ > +/* Functions for running jobs concurrently. > + Copyright (C) 2025 Red Hat, Inc. > + This file is part of elfutils. > + > + This file is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + elfutils 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 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/>. */ > + > +#ifdef HAVE_CONFIG_H > +# include <config.h> > +#endif > + > +#include <error.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdatomic.h> > + > +#include "threadlib.h" > + > +/* Dynamic buffer for thread output. */ > +typedef struct { > + size_t sizeloc; > + char *buf; > + FILE *file; > +} output_stream_t; > + > +/* Allocate resources for STREAM. */ > +static void > +init_thread_output_stream (output_stream_t *stream) > +{ > + stream->buf = NULL; > + stream->sizeloc = 0; > + stream->file = open_memstream (&(stream->buf), &(stream->sizeloc)); > + > + if (stream->file == NULL) > + error (1, 0, _("cannot open thread output stream")); > +} > + > +/* Print and deallocate resources for STREAM. */ > +static void > +print_thread_output_stream (output_stream_t *stream) > +{ > + /* fclose may update stream->buf. */ > + if (fclose (stream->file) != 0) > + error (1, 0, _("cannot close thread output stream")); > + > + printf ("%s", stream->buf); > + free (stream->buf); > +} I think it would be slightly more efficient to use fwrite here. printf "%s" has to first look for the end of buf. And you do have the stream- >sizeloc already. Sorry for stopping the review here suddenly. Will continue later, but hopefully this is already helpful. Cheers, Mark