This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH v3] Make fprintf() function to multithread-safe


Hi, Carlos

On 07/24/2012 10:01 PM, Carlos O'Donell wrote:
>> The test method is OK?
> 
> It is a good start.
> 
> (a) Threading?
> 
> You need two tests.
> 
> You already have the single-thread test case.

The single-thread test result is as follows:
Before the patch, execute the test program with 200 times:
# gcc -o fprintf fprintf_test.c -Wl,-dynamic-linker=/home/pht/sda2/glibc-install/lib/ld-linux-x86-64.so.2 
# perf stat -r 100 -e instructions -- ./fprintf > /dev/null

 Performance counter stats for './fprintf' (100 runs):

     3,837,334,259 instructions              #    0.00  insns per cycle          ( +-  0.00% )

       0.580850804 seconds time elapsed                                          ( +-  0.16% )

# perf stat -r 100 -e instructions -- ./fprintf > /dev/null

 Performance counter stats for './fprintf' (100 runs):

     3,837,347,435 instructions              #    0.00  insns per cycle          ( +-  0.00% )

       0.581234180 seconds time elapsed                                          ( +-  0.19% )


After the patch, execute the test program with 200 times:
# gcc -o fprintf_new fprintf_test.c -Wl,-dynamic-linker=/home/pht/source/glibc-install/lib/ld-linux-x86-64.so.2 
# perf stat -r 100 -e instructions -- ./fprintf_new > /dev/null

 Performance counter stats for './fprintf_new' (100 runs):

     3,853,306,893 instructions              #    0.00  insns per cycle          ( +-  0.00% )

       0.570600865 seconds time elapsed                                          ( +-  0.22% )

# perf stat -r 100 -e instructions -- ./fprintf_new > /dev/null

 Performance counter stats for './fprintf_new' (100 runs):

     3,853,295,107 instructions              #    0.00  insns per cycle          ( +-  0.00% )

       0.568688034 seconds time elapsed                                          ( +-  0.14% )

> 
> You are missing the multi-threaded test case to show the impact of the 
> global lock contention. You should spawn more threads than you have
> CPUs to ensure contention happens sooner, but not so many threads that
> context switching overhead dominates the measurement.
> 

The multi-threaded test result is as follows:
Before the patch, execute the test program with 200 times:
# gcc -o fprintf_mul fprintf_mul_test.c -Wl,-dynamic-linker=/home/pht/sda2/glibc-install/lib/ld-linux-x86-64.so.2 -lpthread
# perf stat -r 100 -e instructions -- ./fprintf_mul > /dev/null

 Performance counter stats for './fprintf_mul' (100 runs):

     3,933,978,985 instructions              #    0.00  insns per cycle          ( +-  0.34% )

       1.652771261 seconds time elapsed                                          ( +-  0.62% )

# perf stat -r 100 -e instructions -- ./fprintf_mul > /dev/null

 Performance counter stats for './fprintf_mul' (100 runs):

     3,954,301,654 instructions              #    0.00  insns per cycle          ( +-  0.35% )

       1.662600499 seconds time elapsed                                          ( +-  0.59% )


After the patch, execute the test program with 200 times:
# gcc -o fprintf_mul_new fprintf_mul_test.c -Wl,-dynamic-linker=/home/pht/source/glibc-install/lib/ld-linux-x86-64.so.2  -lpthread
# perf stat -r 100 -e instructions -- ./fprintf_mul_new > /dev/null

 Performance counter stats for './fprintf_mul_new' (100 runs):

     3,613,283,984 instructions              #    0.00  insns per cycle          ( +-  0.14% )

       1.389559300 seconds time elapsed                                          ( +-  0.22% )

# perf stat -r 100 -e instructions -- ./fprintf_mul_new > /dev/null

 Performance counter stats for './fprintf_mul_new' (100 runs):

     3,615,699,815 instructions              #    0.00  insns per cycle          ( +-  0.15% )

       1.392553792 seconds time elapsed                                          ( +-  0.22% )



# time ./fprintf > /dev/null

real	0m0.589s
user	0m0.584s
sys	0m0.003s
# time ./fprintf_new > /dev/null

real	0m0.572s
user	0m0.567s
sys	0m0.004s
# time ./fprintf_mul > /dev/null

real	0m1.064s
user	0m1.196s
sys	0m0.623s
# time ./fprintf_mul_new > /dev/null

real	0m0.997s
user	0m1.175s
sys	0m0.476s



Test environment:
Kernel: 3.4.6-2.fc17.x86_64
CPU:    Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz
Memory: 8G
OS:     Fedora release 17 (Beefy Miracle)
glibc:  glibc-2.16-ports-merge-90-gbea9b19
patch:  0001-Fix-the-race-between-atexit-and-exit.patch
        0002-Make-fprintf-function-to-multithread-safe.patch

In this test environment, the performance is up after global lock.
I don't understand why:(

The single-thread test program is fprintf_test.c
The multi-thread test program is fprintf_mul_test.c

-- 
Best Regards,
Peng

> They should remain distinct tests.
> 
> (b) Statistics.
> 
> Please run the single-threaded test case hundreds of times.
> 
> Three runs is not sufficient.
> 
> Hundreds of runs will be sufficient to give a high confidence interval
> regardless of the questions we ask later.
> 
> Please run the multi-threaded test case millions of times.
> 
> Why? We are looking to measure the impact of lock contention and if
> the locks aren't contended then it won't measure what we're interested
> in measuring. The number of iterations you need to run is going to
> be inversely proportional to the rate of contention. If you contend
> a lock only once in a thousand acquisitions, then you need to have run
> at least a thousand iterations to measure the impact of the acquisition.
> 
> (c) Document it on the wiki please.
> 
> All performance related patches need their acceptance criteria
> documented on the wiki. It doesn't have to be perfect, but we want
> to capture the history of performance so we can evaluate newer
> patches and methods against the good work you did today.
> 
> Please start documenting here:
> http://sourceware.org/glibc/wiki/benchmarking/results_2_17
> 
> Does all of this make sense?
> 
> Cheers,
> Carlos.
> 

>From e7c0da0ecf06b356a3fb71ff119dceca6708c313 Mon Sep 17 00:00:00 2001
From: Peng Haitao <penght@cn.fujitsu.com>
Date: Tue, 10 Jul 2012 10:12:42 +0800
Subject: [PATCH 1/2] Fix the race between atexit() and exit()
From: Peng Haitao <penght@cn.fujitsu.com>

exit() uses global variable __exit_funcs indirectly, which are not protected.
It is not safe in multithread circumstance.

When call exit() and atexit() simultaneously in multithread circumstance,
the following case will cause unsafe.
The case has main process A and thread B.

a. thread B call atexit()
b. process A call exit() to traverse the __exit_funcs list
c. thread B call calloc() to create a new entry p, and next to listp:
   p->next = *listp;
d. process A modify listp to cur's next, then free cur:
   *listp = cur->next;
e. thread B modify listp to p:
   *listp = p;
f. when get f, the f is undefined:
   const struct exit_function *const f =
     &cur->fns[--cur->idx];
g. programme may be Segmentation fault

Signed-off-by: Peng Haitao <penght@cn.fujitsu.com>
---
 ChangeLog           | 16 ++++++++++++
 stdlib/cxa_atexit.c | 16 ++++++++----
 stdlib/exit.c       | 74 +++++++++++++++++++++++++++++++++++++++++------------
 stdlib/exit.h       | 11 +++++---
 4 files changed, 93 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e608ac4..362d240 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -753,6 +753,22 @@
 	* locale/localeinfo.h: Likewise.
 	(_NL_CURRENT_DEFINE_STRINGIFY): Delete macro.
 	(_NL_CURRENT_DEFINE_STRINGIFY_1): Likewise.
+=
+2012-07-10  Peng Haitao  <penght@cn.fujitsu.com>
+
+	[BZ #14333]
+	* stdlib/cxa_atexit.c: Do not include bits/libc-lock.h.
+	(__libc_lock_define_initialized): Parameter 'lock' changed to global
+	lock '__exit_funcs_lock'.
+	(__new_exitfn): Fail new registration if exit code finished processing
+	all handlers.
+	* stdlib/exit.c: Include atomic.h.
+	(__exit_funcs_done): New flag to indicate status of list traversal.
+	(exit): Changes the handler processing style to that of
+	__cxa_finalize, with locking.
+	* stdlib/exit.h: Include bits/libc-lock.h.
+	(__exit_funcs_lock): Declaration for external access.
+	(__exit_funcs_done): Declaration for external access.
 
 2012-07-09  Roland McGrath  <roland@hack.frob.com>
 
diff --git a/stdlib/cxa_atexit.c b/stdlib/cxa_atexit.c
index 9704398..24f110e 100644
--- a/stdlib/cxa_atexit.c
+++ b/stdlib/cxa_atexit.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999,2001,2002,2005,2006,2009 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2012 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -18,7 +18,6 @@
 #include <assert.h>
 #include <stdlib.h>
 
-#include <bits/libc-lock.h>
 #include "exit.h"
 #include <atomic.h>
 #include <sysdep.h>
@@ -60,7 +59,7 @@ INTDEF(__cxa_atexit)
 
 
 /* We change global data, so we need locking.  */
-__libc_lock_define_initialized (static, lock)
+__libc_lock_define_initialized (, __exit_funcs_lock)
 
 
 static struct exit_function_list initial;
@@ -75,7 +74,14 @@ __new_exitfn (struct exit_function_list **listp)
   struct exit_function *r = NULL;
   size_t i = 0;
 
-  __libc_lock_lock (lock);
+  __libc_lock_lock (__exit_funcs_lock);
+
+  if (__exit_funcs_done)
+  {
+    /* exit code finished processing all handlers, so fail registration.  */
+    __libc_lock_unlock (__exit_funcs_lock);
+    return NULL;
+  }
 
   for (l = *listp; l != NULL; p = l, l = l->next)
     {
@@ -126,7 +132,7 @@ __new_exitfn (struct exit_function_list **listp)
       ++__new_exitfn_called;
     }
 
-  __libc_lock_unlock (lock);
+  __libc_lock_unlock (__exit_funcs_lock);
 
   return r;
 }
diff --git a/stdlib/exit.c b/stdlib/exit.c
index 1ad548f..d686de5 100644
--- a/stdlib/exit.c
+++ b/stdlib/exit.c
@@ -1,5 +1,4 @@
-/* Copyright (C) 1991,95,96,97,99,2001,2002,2005,2009
-   Free Software Foundation, Inc.
+/* Copyright (C) 1991-2012 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -20,11 +19,14 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sysdep.h>
+#include <atomic.h>
 #include "exit.h"
 
 #include "set-hooks.h"
 DEFINE_HOOK (__libc_atexit, (void))
 
+/* initialize the processing complete flag to false.  */
+bool __exit_funcs_done = false;
 
 /* Call all functions registered with `atexit' and `on_exit',
    in the reverse of the order in which they were registered
@@ -38,25 +40,44 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
      the functions registered with `atexit' and `on_exit'. We call
      everyone on the list and use the status value in the last
      exit (). */
-  while (*listp != NULL)
+  while (1)
     {
-      struct exit_function_list *cur = *listp;
+      struct exit_function_list *cur;
+      struct exit_function *f;
 
-      while (cur->idx > 0)
-	{
-	  const struct exit_function *const f =
-	    &cur->fns[--cur->idx];
-	  switch (f->flavor)
+restart:
+      __libc_lock_lock (__exit_funcs_lock);
+
+      cur = *listp;
+      if (cur == NULL)
+        {
+          /* mark the processing complete,
+             we won't allow to register more handlers.  */
+          __exit_funcs_done = true;
+          __libc_lock_unlock (__exit_funcs_lock);
+          break;
+        }
+
+      f = &cur->fns[cur->idx];
+      uint64_t check = __new_exitfn_called;
+
+      __libc_lock_unlock (__exit_funcs_lock);
+
+      while (f > &cur->fns[0])
+        {
+	  switch ((--f)->flavor)
 	    {
 	      void (*atfct) (void);
 	      void (*onfct) (int status, void *arg);
 	      void (*cxafct) (void *arg, int status);
 
 	    case ef_free:
+              break;
 	    case ef_us:
-	      break;
+	      goto restart;
 	    case ef_on:
 	      onfct = f->func.on.fn;
+              atomic_compare_and_exchange_bool_acq (&f->flavor, ef_free, ef_on);
 #ifdef PTR_DEMANGLE
 	      PTR_DEMANGLE (onfct);
 #endif
@@ -64,6 +85,7 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
 	      break;
 	    case ef_at:
 	      atfct = f->func.at;
+              atomic_compare_and_exchange_bool_acq (&f->flavor, ef_free, ef_at);
 #ifdef PTR_DEMANGLE
 	      PTR_DEMANGLE (atfct);
 #endif
@@ -71,20 +93,40 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
 	      break;
 	    case ef_cxa:
 	      cxafct = f->func.cxa.fn;
+              atomic_compare_and_exchange_bool_acq (&f->flavor, ef_free, ef_cxa);
 #ifdef PTR_DEMANGLE
 	      PTR_DEMANGLE (cxafct);
 #endif
 	      cxafct (f->func.cxa.arg, status);
 	      break;
 	    }
+          /* It is possible that last exit function registered
+             more exit functions. Start the loop over. */
+          __libc_lock_lock (__exit_funcs_lock);
+          if (__builtin_expect (check != __new_exitfn_called, 0))
+            {
+              __libc_lock_unlock (__exit_funcs_lock);
+              goto restart;
+            }
+	  __libc_lock_unlock (__exit_funcs_lock);
 	}
 
-      *listp = cur->next;
-      if (*listp != NULL)
-	/* Don't free the last element in the chain, this is the statically
-	   allocate element.  */
-	free (cur);
-    }
+      __libc_lock_lock (__exit_funcs_lock);
+      if (__builtin_expect (check != __new_exitfn_called, 0))
+        {
+          __libc_lock_unlock (__exit_funcs_lock);
+          goto restart;
+        }
+      else
+        {
+          *listp = cur->next;
+          if (*listp != NULL)
+	  /* Don't free the last element in the chain, this is the statically
+	     allocate element.  */
+	    free (cur);
+          __libc_lock_unlock (__exit_funcs_lock);
+        }
+  }
 
   if (run_list_atexit)
     RUN_HOOK (__libc_atexit, ());
diff --git a/stdlib/exit.h b/stdlib/exit.h
index 818c7ac..25a398e 100644
--- a/stdlib/exit.h
+++ b/stdlib/exit.h
@@ -21,6 +21,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <bits/libc-lock.h>
 
 enum
 {
@@ -66,12 +67,16 @@ extern uint64_t __new_exitfn_called attribute_hidden;
 
 extern void __run_exit_handlers (int status, struct exit_function_list **listp,
 				 bool run_list_atexit)
-  attribute_hidden __attribute__ ((__noreturn__));
+	  attribute_hidden __attribute__ ((__noreturn__));
 
 extern int __internal_atexit (void (*func) (void *), void *arg, void *d,
-			      struct exit_function_list **listp)
-  attribute_hidden;
+      struct exit_function_list **listp)
+	  attribute_hidden;
 extern int __cxa_at_quick_exit (void (*func) (void *), void *d);
 
+__libc_lock_define (extern, __exit_funcs_lock);
+
+/* flag to mark that all registered atexit/on_exit handlers are called.  */
+extern bool __exit_funcs_done;
 
 #endif	/* exit.h  */
-- 
1.7.11.2

>From 6dfce023358066cd09e170101b7d49dd7fdc7a03 Mon Sep 17 00:00:00 2001
From: Peng Haitao <penght@cn.fujitsu.com>
Date: Tue, 10 Jul 2012 11:20:56 +0800
Subject: [PATCH 2/2] Make fprintf() function to multithread-safe
From: Peng Haitao <penght@cn.fujitsu.com>

fprintf() uses static variables __printf_function_table and
__printf_va_arg_table indirectly, which are not protected. It is
not safe in multithread circumstance.

When call fprintf() and register_printf_specifier() simultaneously
in multithread circumstance, the following case will cause unsafe.
The case has two threads: A and B.

a. thread B call register_printf_specifier('W', print_widget, print_widget_arginfo)
b. thread A call fprintf (stdout, "|%W|\n", &mywidget), when judge
   __printf_function_table is not NULL, but not output &mywidget
c. thread B call register_printf_specifier('W', NULL, NULL)
d. thread A output &mywidget when __printf_function_table is NULL
e. programme will Segmentation fault

Signed-off-by: Peng Haitao <penght@cn.fujitsu.com>
---
 ChangeLog                 | 12 ++++++++++++
 stdio-common/reg-printf.c |  6 +++---
 stdio-common/reg-type.c   |  6 +++---
 stdio-common/vfprintf.c   |  6 ++++++
 4 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 362d240..3be57ec 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -756,6 +756,18 @@
 =
 2012-07-10  Peng Haitao  <penght@cn.fujitsu.com>
 
+	[BZ #14267]
+	* stdio-common/reg-printf.c: __libc_lock_define_initialized changed to
+	__libc_rwlock_define_initialized.
+	Parameter 'lock' changed to global lock 'lock_fun_type'.
+	* stdio-common/reg-type.c: __libc_lock_define_initialized changed to
+	__libc_rwlock_define.
+	Parameter 'lock' changed to global lock 'lock_fun_type'.
+	* stdio-common/vfprintf.c (vfprintf): Lock around use of
+	__printf_function_table and __printf_va_arg_table.
+
+2012-07-10  Peng Haitao  <penght@cn.fujitsu.com>
+
 	[BZ #14333]
 	* stdlib/cxa_atexit.c: Do not include bits/libc-lock.h.
 	(__libc_lock_define_initialized): Parameter 'lock' changed to global
diff --git a/stdio-common/reg-printf.c b/stdio-common/reg-printf.c
index 30bf7da..56e2b37 100644
--- a/stdio-common/reg-printf.c
+++ b/stdio-common/reg-printf.c
@@ -28,7 +28,7 @@ libc_freeres_ptr (printf_arginfo_size_function **__printf_arginfo_table)
   attribute_hidden;
 printf_function **__printf_function_table attribute_hidden;
 
-__libc_lock_define_initialized (static, lock)
+__libc_rwlock_define_initialized (, lock_fun_type);
 
 int __register_printf_specifier (int, printf_function,
 				 printf_arginfo_size_function);
@@ -50,7 +50,7 @@ __register_printf_specifier (spec, converter, arginfo)
     }
 
   int result = 0;
-  __libc_lock_lock (lock);
+  __libc_rwlock_wrlock (lock_fun_type);
 
   if (__printf_function_table == NULL)
     {
@@ -70,7 +70,7 @@ __register_printf_specifier (spec, converter, arginfo)
   __printf_arginfo_table[spec] = arginfo;
 
  out:
-  __libc_lock_unlock (lock);
+  __libc_rwlock_unlock (lock_fun_type);
 
   return result;
 }
diff --git a/stdio-common/reg-type.c b/stdio-common/reg-type.c
index 31c7472..bd55e9c 100644
--- a/stdio-common/reg-type.c
+++ b/stdio-common/reg-type.c
@@ -25,7 +25,7 @@
 libc_freeres_ptr (printf_va_arg_function **__printf_va_arg_table)
   attribute_hidden;
 
-__libc_lock_define_initialized (static, lock);
+__libc_rwlock_define (extern, lock_fun_type);
 
 /* Last type allocated.  */
 static int pa_next_type = PA_LAST;
@@ -35,7 +35,7 @@ int
 __register_printf_type (printf_va_arg_function fct)
 {
   int result = -1;
-  __libc_lock_lock (lock);
+  __libc_rwlock_wrlock (lock_fun_type);
 
   if (__printf_va_arg_table == NULL)
     {
@@ -54,7 +54,7 @@ __register_printf_type (printf_va_arg_function fct)
     }
 
  out:
-  __libc_lock_unlock (lock);
+  __libc_rwlock_unlock (lock_fun_type);
 
   return result;
 }
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index d569034..5d082cf 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -195,6 +195,7 @@ static CHAR_T *group_number (CHAR_T *, CHAR_T *, const char *, const char *)
      __THROW internal_function;
 #endif
 
+__libc_rwlock_define (extern, lock_fun_type);
 
 /* The function itself.  */
 int
@@ -1319,6 +1320,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
   _IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
   _IO_flockfile (s);
 
+  __libc_rwlock_rdlock (lock_fun_type);
+
   /* Write the literal text before the first format.  */
   outstring ((const UCHAR_T *) format,
 	     lead_str_end - (const UCHAR_T *) format);
@@ -2047,6 +2050,9 @@ do_positional:
 all_done:
   free (args_malloced);
   free (workstart);
+
+  __libc_rwlock_unlock (lock_fun_type);
+
   /* Unlock the stream.  */
   _IO_funlockfile (s);
   _IO_cleanup_region_end (0);
-- 
1.7.11.2

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <printf.h>
#include <pthread.h>
#include <unistd.h>

#define MAX_COUNT 100000

typedef struct
{
	char *name;
} Widget;

int print_widget (FILE *stream, const struct printf_info *info,
		  const void *const *args)
{
	const Widget *w;
	char *buffer;
	int len;

	/* Format the output into a string. */
	w = *((const Widget **) (args[0]));
	len = asprintf (&buffer, "<Widget %p: %s>", w, w->name);
	if (len == -1)
		return -1;

	/* Pad to the minimum field width and print to the stream. */
	len = fprintf (stream, "%*s",
		      (info->left ? -info->width : info->width), buffer);

	/* Clean up and return. */
	free (buffer);
	return len;
}

int print_widget_arginfo (const struct printf_info *info, size_t n,
			  int *argtypes)
{
	/* We always take exactly one argument and this is a pointer to the
	*  structure. */
	if (n > 0)
		argtypes[0] = PA_POINTER;
	return 1;
}

void *thread_fn()
{
	Widget mywidget;
	mywidget.name = "mywidget";
	long long int i = 0;

	while (i++ < MAX_COUNT)
		fprintf (stdout, "|%W|\n", &mywidget);

	return ((void *)0);
}

int main (void)
{
	int i, status;
	int cpus = sysconf (_SC_NPROCESSORS_ONLN) + 1;
	pthread_t threads[cpus];

	/* Register the print function for widgets. */
	register_printf_specifier('W', print_widget,
		(printf_arginfo_size_function*) print_widget_arginfo);

	for (i = 0; i < cpus; i++) {
		status = pthread_create(&threads[i], NULL, thread_fn, NULL);
		if (status != 0) {
			perror ("pthread_create");
			exit (1);
		}
	}

	for (i = 0; i < cpus; i++) {
		status = pthread_join(threads[i], NULL);
		if (status != 0) {
			perror ("pthread_join");
			exit (1);
		}
	}

	return 0;
}

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <printf.h>

#define MAX_COUNT 1000000

typedef struct
{
	char *name;
} Widget;

int print_widget (FILE *stream, const struct printf_info *info,
		  const void *const *args)
{
	const Widget *w;
	char *buffer;
	int len;

	/* Format the output into a string. */
	w = *((const Widget **) (args[0]));
	len = asprintf (&buffer, "<Widget %p: %s>", w, w->name);
	if (len == -1)
		return -1;

	/* Pad to the minimum field width and print to the stream. */
	len = fprintf (stream, "%*s",
		      (info->left ? -info->width : info->width), buffer);

	/* Clean up and return. */
	free (buffer);
	return len;
}

int print_widget_arginfo (const struct printf_info *info, size_t n,
			  int *argtypes)
{
	/* We always take exactly one argument and this is a pointer to the
	*  structure. */
	if (n > 0)
		argtypes[0] = PA_POINTER;
	return 1;
}
 
int main (void)
{
	int i = 0;
	/* Make a widget to print. */
	Widget mywidget;
	mywidget.name = "mywidget";

	/* Register the print function for widgets. */
	register_printf_specifier ('W', print_widget,
				  (printf_arginfo_size_function*) print_widget_arginfo);

	while (i++ < MAX_COUNT)
		fprintf (stdout, "|%W|\n", &mywidget);

	return 0;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]