2003-06-17  Jakub Jelinek  <jakub@redhat.com>

	* posix/regcomp.c (build_word_op): Use alnum instead of alpha class.
This commit is contained in:
Ulrich Drepper 2003-06-17 22:40:05 +00:00
parent 60d73a7ac4
commit f23b30e23b
23 changed files with 838 additions and 98 deletions

View File

@ -1,3 +1,7 @@
2003-06-17 Jakub Jelinek <jakub@redhat.com>
* posix/regcomp.c (build_word_op): Use alnum instead of alpha class.
2003-06-17 Ulrich Drepper <drepper@redhat.com>
* sysdeps/unix/clock_nanosleep.c (clock_nanosleep): nanosleep

View File

@ -1,3 +1,28 @@
2003-06-18 Jakub Jelinek <jakub@redhat.com>
* internals.h (__librt_multiple_threads, __librt_enable_asynccancel,
__librt_disable_asynccancel): Declare.
(LIBC_CANCEL_ASYNC, LIBC_CANCEL_RESET, LIBC_CANCEL_HANDLED): Define
for IS_IN_librt.
* sysdeps/unix/sysv/linux/i386/sysdep-cancel.h: Support cancellation
in librt.
* sysdeps/unix/sysv/linux/ia64/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/s390/s390-32/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/sparc/sparc32/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/sparc/sparc64/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/x86_64/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/powerpc/powerpc32/sysdep-cancel.h: Likewise.
* sysdeps/unix/sysv/linux/powerpc/powerpc64/sysdep-cancel.h: Likewise.
* sysdeps/x86_64/tcb-offsets.sym: New file.
* sysdeps/x86_64/Makefile: New file.
* sysdeps/x86_64/tls.h (tcbhead_t): Add multiple_threads.
* Versions (libc): Export __librt_enable_asynccancel,
__librt_disable_asynccancel and __librt_multiple_threads as
GLIBC_PRIVATE.
* libc-cancellation.c (__librt_multiple_threads,
__librt_enable_asynccancel, __librt_disable_asynccancel): New aliases.
2003-06-12 Steven Munroe <sjmunroe@us.ibm.com>
* sysdeps/unix/sysv/linux/powerpc/powerpc64/sysdep-cancel.h

View File

@ -31,7 +31,8 @@ libc {
__libc_creat; __libc_poll; __libc_pselect; __libc_select;
__libc_sigpause; __libc_sigsuspend; __libc_sigwait; __libc_sigwaitinfo;
__libc_waitid; __libc___xpg_sigpause;
__libc_waitid; __libc___xpg_sigpause; __librt_enable_asynccancel;
__librt_disable_asynccancel; __librt_multiple_threads;
}
}

View File

@ -257,6 +257,7 @@ static inline int nonexisting_handle(pthread_handle h, pthread_t id)
/* Defined and used in libc.so. */
extern int __libc_multiple_threads attribute_hidden;
extern int __librt_multiple_threads;
/* Debugging */
@ -468,6 +469,10 @@ extern int __libc_enable_asynccancel (void) attribute_hidden;
extern void __libc_disable_asynccancel (int oldtype)
internal_function attribute_hidden;
/* The two functions are in libc.so and are exported. */
extern int __librt_enable_asynccancel (void);
extern void __librt_disable_asynccancel (int oldtype) internal_function;
extern void __pthread_cleanup_upto (__jmp_buf target,
char *targetframe) attribute_hidden;
extern pid_t __pthread_fork (struct fork_block *b) attribute_hidden;
@ -480,7 +485,7 @@ extern pid_t __pthread_fork (struct fork_block *b) attribute_hidden;
# define LIBC_CANCEL_HANDLED() \
__asm (".globl " __SYMBOL_PREFIX "__libc_enable_asynccancel"); \
__asm (".globl " __SYMBOL_PREFIX "__libc_disable_asynccancel")
#elif defined NOT_IN_libc && defined IS_IN_libpthread
#elif defined IS_IN_libpthread
# define LIBC_CANCEL_ASYNC() \
__pthread_enable_asynccancel ()
# define LIBC_CANCEL_RESET(oldtype) \
@ -488,6 +493,14 @@ extern pid_t __pthread_fork (struct fork_block *b) attribute_hidden;
# define LIBC_CANCEL_HANDLED() \
__asm (".globl " __SYMBOL_PREFIX "__pthread_enable_asynccancel"); \
__asm (".globl " __SYMBOL_PREFIX "__pthread_disable_asynccancel")
#elif defined IS_IN_librt
# define LIBC_CANCEL_ASYNC() \
__librt_enable_asynccancel ()
# define LIBC_CANCEL_RESET(oldtype) \
__librt_disable_asynccancel (oldtype)
# define LIBC_CANCEL_HANDLED() \
__asm (".globl " __SYMBOL_PREFIX "__librt_enable_asynccancel"); \
__asm (".globl " __SYMBOL_PREFIX "__librt_disable_asynccancel")
#else
# define LIBC_CANCEL_ASYNC() 0 /* Just a dummy value. */
# define LIBC_CANCEL_RESET(val) ((void)(val)) /* Nothing, but evaluate it. */

View File

@ -31,7 +31,8 @@
weak_extern (__pthread_do_exit)
# endif
int __libc_multiple_threads attribute_hidden;
int __libc_multiple_threads attribute_hidden __attribute__((nocommon));
strong_alias (__libc_multiple_threads, __librt_multiple_threads);
/* The next two functions are similar to pthread_setcanceltype() but
more specialized for the use in the cancelable functions like write().
@ -49,6 +50,7 @@ __libc_enable_asynccancel (void)
(PTHREAD_CANCELED, CURRENT_STACK_FRAME), 0);
return oldtype;
}
strong_alias (__libc_enable_asynccancel, __librt_enable_asynccancel)
void
internal_function attribute_hidden
@ -57,5 +59,6 @@ __libc_disable_asynccancel (int oldtype)
pthread_descr self = thread_self();
LIBC_THREAD_SETMEM(self, p_canceltype, oldtype);
}
strong_alias (__libc_disable_asynccancel, __librt_disable_asynccancel)
#endif

View File

@ -24,7 +24,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -108,8 +108,10 @@
#if !defined NOT_IN_libc
# define __local_multiple_threads __libc_multiple_threads
#else
#elif defined IS_IN_libpthread
# define __local_multiple_threads __pthread_multiple_threads
#else
# define __local_multiple_threads __librt_multiple_threads
#endif
# ifndef __ASSEMBLER__
@ -118,7 +120,12 @@
__builtin_expect (THREAD_GETMEM (THREAD_SELF, \
p_header.data.multiple_threads) == 0, 1)
# else
extern int __local_multiple_threads attribute_hidden;
extern int __local_multiple_threads
# if !defined NOT_IN_libc || defined IS_IN_libpthread
attribute_hidden;
# else
;
# endif
# define SINGLE_THREAD_P __builtin_expect (__local_multiple_threads == 0, 1)
# endif
# else
@ -127,16 +134,23 @@ extern int __local_multiple_threads attribute_hidden;
# elif defined FLOATING_STACKS && USE___THREAD
# define SINGLE_THREAD_P cmpl $0, %gs:MULTIPLE_THREADS_OFFSET
# else
# if !defined NOT_IN_libc || defined IS_IN_libpthread
# define __SINGLE_THREAD_CMP cmpl $0, __local_multiple_threads@GOTOFF(%ecx)
# else
# define __SINGLE_THREAD_CMP \
movl __local_multiple_threads@GOT(%ecx), %ecx;\
cmpl $0, (%ecx)
# endif
# if !defined HAVE_HIDDEN || !USE___THREAD
# define SINGLE_THREAD_P \
SETUP_PIC_REG (cx); \
addl $_GLOBAL_OFFSET_TABLE_, %ecx; \
cmpl $0, __local_multiple_threads@GOTOFF(%ecx)
__SINGLE_THREAD_CMP
# else
# define SINGLE_THREAD_P \
call __i686.get_pc_thunk.cx; \
addl $_GLOBAL_OFFSET_TABLE_, %ecx; \
cmpl $0, __local_multiple_threads@GOTOFF(%ecx)
__SINGLE_THREAD_CMP
# endif
# endif
# endif

View File

@ -23,7 +23,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -85,9 +85,12 @@ __syscall_error_##args: \
# ifdef IS_IN_libpthread
# define CENABLE br.call.sptk.many b0 = __pthread_enable_asynccancel
# define CDISABLE br.call.sptk.many b0 = __pthread_disable_asynccancel
# else
# elif !defined NOT_IN_libc
# define CENABLE br.call.sptk.many b0 = __libc_enable_asynccancel
# define CDISABLE br.call.sptk.many b0 = __libc_disable_asynccancel
# else
# define CENABLE br.call.sptk.many b0 = __librt_enable_asynccancel
# define CDISABLE br.call.sptk.many b0 = __librt_disable_asynccancel
# endif
#define COPY_ARGS_0 /* Nothing */

View File

@ -23,7 +23,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -78,9 +78,12 @@
# ifdef IS_IN_libpthread
# define CENABLE bl JUMPTARGET(__pthread_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__pthread_disable_asynccancel)
# else
# elif !defined NOT_IN_libc
# define CENABLE bl JUMPTARGET(__libc_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__libc_disable_asynccancel)
# else
# define CENABLE bl JUMPTARGET(__librt_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__librt_disable_asynccancel)
# endif
# ifndef __ASSEMBLER__

View File

@ -22,7 +22,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -78,14 +78,23 @@
# define CENABLE bl JUMPTARGET(__pthread_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__pthread_disable_asynccancel)
# define __local_multiple_threads __pthread_multiple_threads
# else
# elif !defined NOT_IN_libc
# define CENABLE bl JUMPTARGET(__libc_enable_asynccancel)
# define CDISABLE bl JUMPTARGET(__libc_disable_asynccancel)
# define __local_multiple_threads __libc_multiple_threads
# else
# define CENABLE bl JUMPTARGET(__librt_enable_asynccancel); nop
# define CDISABLE bl JUMPTARGET(__librt_disable_asynccancel); nop
# define __local_multiple_threads __librt_multiple_threads
# endif
# ifndef __ASSEMBLER__
extern int __local_multiple_threads attribute_hidden;
extern int __local_multiple_threads
# if !defined NOT_IN_libc || defined IS_IN_libpthread
attribute_hidden;
# else
;
# endif
# define SINGLE_THREAD_P __builtin_expect (__local_multiple_threads == 0, 1)
# else
# define SINGLE_THREAD_P \

View File

@ -23,7 +23,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -62,9 +62,12 @@ L(pseudo_end):
# ifdef IS_IN_libpthread
# define CENABLE __pthread_enable_asynccancel
# define CDISABLE __pthread_disable_asynccancel
# else
# elif !defined NOT_IN_libc
# define CENABLE __libc_enable_asynccancel
# define CDISABLE __libc_disable_asynccancel
# else
# define CENABLE __librt_enable_asynccancel
# define CDISABLE __librt_disable_asynccancel
# endif
#define STM_0 /* Nothing */

View File

@ -23,7 +23,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -58,10 +58,13 @@ L(pseudo_end):
# define CENABLE __pthread_enable_asynccancel
# define CDISABLE __pthread_disable_asynccancel
# define __local_multiple_threads __pthread_multiple_threads
# else
# elif !defined NOT_IN_libc
# define CENABLE __libc_enable_asynccancel
# define CDISABLE __libc_disable_asynccancel
# define __local_multiple_threads __libc_multiple_threads
# else
# define CENABLE __librt_enable_asynccancel
# define CDISABLE __librt_disable_asynccancel
# endif
#define STM_0 /* Nothing */
@ -78,14 +81,31 @@ L(pseudo_end):
#define LM_4 lmg %r2,%r5,16+160(%r15);
#define LM_5 lmg %r2,%r5,16+160(%r15);
# ifndef __ASSEMBLER__
# if !defined NOT_IN_libc || defined IS_IN_libpthread
# ifndef __ASSEMBLER__
extern int __local_multiple_threads attribute_hidden;
# define SINGLE_THREAD_P \
# define SINGLE_THREAD_P \
__builtin_expect (__local_multiple_threads == 0, 1)
# else
# define SINGLE_THREAD_P \
# else
# define SINGLE_THREAD_P \
larl %r1,__local_multiple_threads; \
icm %r0,15,0(%r1);
# endif
# else
# ifndef __ASSEMBLER__
# define SINGLE_THREAD_P \
__builtin_expect (THREAD_GETMEM (THREAD_SELF, \
p_header.data.multiple_threads) == 0, 1)
# else
# define SINGLE_THREAD_P \
ear %r1,%a0; \
sllg %r1,%r1,32; \
ear %r1,%a1; \
icm %r1,15,MULTIPLE_THREADS_OFFSET(%r1);
# endif
# endif
#elif !defined __ASSEMBLER__

View File

@ -23,7 +23,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -68,9 +68,12 @@ SYSCALL_ERROR_HANDLER_ENTRY(__syscall_error_handler2) \
# ifdef IS_IN_libpthread
# define CENABLE call __pthread_enable_asynccancel
# define CDISABLE call __pthread_disable_asynccancel
# else
# elif defined !NOT_IN_libc
# define CENABLE call __libc_enable_asynccancel
# define CDISABLE call __libc_disable_asynccancel
# else
# define CENABLE call __librt_enable_asynccancel
# define CDISABLE call __librt_disable_asynccancel
# endif
#define COPY_ARGS_0 /* Nothing */

View File

@ -23,7 +23,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -67,9 +67,12 @@ SYSCALL_ERROR_HANDLER_ENTRY(__syscall_error_handler2) \
# ifdef IS_IN_libpthread
# define CENABLE call __pthread_enable_asynccancel
# define CDISABLE call __pthread_disable_asynccancel
# else
# elif !defined NOT_IN_libc
# define CENABLE call __libc_enable_asynccancel
# define CDISABLE call __libc_disable_asynccancel
# else
# define CENABLE call __librt_enable_asynccancel
# define CDISABLE call __librt_disable_asynccancel
# endif
#define COPY_ARGS_0 /* Nothing */

View File

@ -24,7 +24,7 @@
# include <linuxthreads/internals.h>
#endif
#if !defined NOT_IN_libc || defined IS_IN_libpthread
#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
# undef PSEUDO
# define PSEUDO(name, syscall_name, args) \
@ -94,18 +94,34 @@
# define CENABLE call __pthread_enable_asynccancel;
# define CDISABLE call __pthread_disable_asynccancel;
# define __local_multiple_threads __pthread_multiple_threads
# else
# elif !defined NOT_IN_libc
# define CENABLE call __libc_enable_asynccancel;
# define CDISABLE call __libc_disable_asynccancel;
# define __local_multiple_threads __libc_multiple_threads
# else
# define CENABLE call __librt_enable_asynccancel@plt;
# define CDISABLE call __librt_disable_asynccancel@plt;
# endif
# ifndef __ASSEMBLER__
# if defined IS_IN_libpthread || !defined NOT_IN_libc
# ifndef __ASSEMBLER__
extern int __local_multiple_threads attribute_hidden;
# define SINGLE_THREAD_P \
__builtin_expect (__local_multiple_threads == 0, 1)
# else
# define SINGLE_THREAD_P cmpl $0, __local_multiple_threads(%rip)
# endif
# else
# define SINGLE_THREAD_P cmpl $0, __local_multiple_threads(%rip)
# ifndef __ASSEMBLER__
# define SINGLE_THREAD_P \
__builtin_expect (THREAD_GETMEM (THREAD_SELF, \
p_header.data.multiple_threads) == 0, 1)
# else
# define SINGLE_THREAD_P cmpl $0, %fs:MULTIPLE_THREADS_OFFSET
# endif
# endif
#elif !defined __ASSEMBLER__

View File

@ -0,0 +1,3 @@
ifeq ($(subdir),csu)
gen-as-const-headers += tcb-offsets.sym
endif

View File

@ -0,0 +1,4 @@
#include <sysdep.h>
#include <tls.h>
MULTIPLE_THREADS_OFFSET offsetof (tcbhead_t, multiple_threads)

View File

@ -39,6 +39,7 @@ typedef struct
thread descriptor used by libpthread. */
dtv_t *dtv;
void *self; /* Pointer to the thread descriptor. */
int multiple_threads;
} tcbhead_t;
#endif

View File

@ -1,5 +1,20 @@
2003-06-17 Ulrich Drepper <drepper@redhat.com>
* Makefile: Add rules to build and run tst-cancel18 and tst-cancelx18.
* tst-cancel18.c: New file.
* tst-cancelx18.c: New file.
* tst-cancel4.c: Test connect, creat, msgrcv, msgsnd, sendmsg, sendto,
and tcdrain.
* Makefile: Add rules to build and run tst-cancel17 and tst-cancel17x.
* tst-cancel17.c: New file.
* tst-cancelx17.c: New file.
* sysdeps/unix/sysv/linux/sigtimedwait.c: New file.
* sysdeps/unix/sysv/linux/sigwait.c: New file.
* sysdeps/unix/sysv/linux/sigwaitinfo.c: New file.
* tst-cancel4.c: Test open, close, pread, pwrite, fsync, and msync.
2003-06-16 Jakub Jelinek <jakub@redhat.com>

View File

@ -212,7 +212,7 @@ tests = tst-attr1 tst-attr2 \
tst-cancel1 tst-cancel2 tst-cancel3 tst-cancel4 tst-cancel5 \
tst-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 tst-cancel10 \
tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \
tst-cancel16 tst-cancel17 \
tst-cancel16 tst-cancel17 tst-cancel18 \
tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \
tst-flock1 tst-flock2 \
tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \
@ -243,7 +243,7 @@ ifeq ($(have-forced-unwind),yes)
tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
tst-cancelx6 tst-cancelx7 tst-cancelx8 tst-cancelx9 tst-cancelx10 \
tst-cancelx11 tst-cancelx12 tst-cancelx13 tst-cancelx14 tst-cancelx15\
tst-cancelx16 tst-cancelx17 \
tst-cancelx16 tst-cancelx17 tst-cancelx18 \
tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3
endif
ifeq ($(build-shared),yes)
@ -388,10 +388,14 @@ ifeq (yes,$(build-shared))
$(objpfx)tst-cond11: $(common-objpfx)rt/librt.so
$(objpfx)tst-cancel17: $(common-objpfx)rt/librt.so
$(objpfx)tst-cancelx17: $(common-objpfx)rt/librt.so
$(objpfx)tst-cancel18: $(common-objpfx)rt/librt.so
$(objpfx)tst-cancelx18: $(common-objpfx)rt/librt.so
else
$(objpfx)tst-cond11: $(common-objpfx)rt/librt.a
$(objpfx)tst-cancel17: $(common-objpfx)rt/librt.a
$(objpfx)tst-cancelx17: $(common-objpfx)rt/librt.a
$(objpfx)tst-cancel18: $(common-objpfx)rt/librt.a
$(objpfx)tst-cancelx18: $(common-objpfx)rt/librt.a
endif
extra-B-pthread.so = -B$(common-objpfx)nptl/

174
nptl/tst-cancel18.c Normal file
View File

@ -0,0 +1,174 @@
/* Copyright (C) 2003 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
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 Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static pthread_barrier_t b;
/* Cleanup handling test. */
static int cl_called;
static void
cl (void *arg)
{
++cl_called;
}
static void *
tf (void *arg)
{
int r = pthread_barrier_wait (&b);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
puts ("barrier_wait failed");
exit (1);
}
pthread_cleanup_push (cl, NULL);
struct timespec ts = { .tv_sec = arg == NULL ? 10000000 : 0, .tv_nsec = 0 };
TEMP_FAILURE_RETRY (clock_nanosleep (CLOCK_REALTIME, 0, &ts, &ts));
pthread_cleanup_pop (0);
puts ("clock_nanosleep returned");
exit (1);
}
static int
do_test (void)
{
if (pthread_barrier_init (&b, NULL, 2) != 0)
{
puts ("barrier_init failed");
return 1;
}
pthread_t th;
if (pthread_create (&th, NULL, tf, NULL) != 0)
{
puts ("1st create failed");
return 1;
}
int r = pthread_barrier_wait (&b);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
puts ("barrier_wait failed");
exit (1);
}
struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
while (nanosleep (&ts, &ts) != 0)
continue;
puts ("going to cancel in-time");
if (pthread_cancel (th) != 0)
{
puts ("1st cancel failed");
return 1;
}
void *status;
if (pthread_join (th, &status) != 0)
{
puts ("1st join failed");
return 1;
}
if (status != PTHREAD_CANCELED)
{
puts ("1st thread not canceled");
return 1;
}
if (cl_called == 0)
{
puts ("cleanup handler not called");
return 1;
}
if (cl_called > 1)
{
puts ("cleanup handler called more than once");
return 1;
}
puts ("in-time cancellation succeeded");
cl_called = 0;
if (pthread_create (&th, NULL, tf, NULL) != 0)
{
puts ("2nd create failed");
return 1;
}
puts ("going to cancel early");
if (pthread_cancel (th) != 0)
{
puts ("2nd cancel failed");
return 1;
}
r = pthread_barrier_wait (&b);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
puts ("barrier_wait failed");
exit (1);
}
if (pthread_join (th, &status) != 0)
{
puts ("2nd join failed");
return 1;
}
if (status != PTHREAD_CANCELED)
{
puts ("2nd thread not canceled");
return 1;
}
if (cl_called == 0)
{
printf ("cleanup handler not called\n");
return 1;
}
if (cl_called > 1)
{
printf ("cleanup handler called more than once\n");
return 1;
}
puts ("early cancellation succeeded");
return 0;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"

View File

@ -28,8 +28,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/msg.h>
#include <sys/poll.h>
#include <sys/select.h>
#include <sys/socket.h>
@ -40,17 +42,9 @@
#include "pthreadP.h"
/* The following interfaces are defined to be cancellation points but
tests are not yet implemented:
aio_suspend() clock_nanosleep()
connect() creat()
msgrcv() msgsnd()
sendmsg() sendto()
tcdrain()
Since STREAMS are not supported in the standard Linux kernel there
is no need to test the STREAMS related functions. This affects
/* Since STREAMS are not supported in the standard Linux kernel and
there we don't advertise STREAMS as supported is no need to test
the STREAMS related functions. This affects
getmsg() getpmsg() putmsg()
putpmsg()
@ -65,6 +59,10 @@
POSIX message queues aren't implemented yet. This affects
mq_receive() mq_send() mq_timedreceive() mq_timedsend()
aio_suspend() is tested in tst-cancel17.
clock_nanosleep() is tested in tst-cancel18.
*/
/* Pipe descriptors. */
@ -75,6 +73,8 @@ static int tempfd = -1;
static int tempfd2 = -1;
/* Name of temporary file to be removed after each round. */
static char *tempfname;
/* Temporary message queue. */
static int tempmsg = -1;
/* Often used barrier for two threads. */
static pthread_barrier_t b2;
@ -121,15 +121,16 @@ tf_read (void *arg)
exit (1);
}
ssize_t s;
pthread_cleanup_push (cl, NULL);
char buf[100];
ssize_t s = read (fd, buf, sizeof (buf));
printf ("%s: read returns with %zd\n", __FUNCTION__, s);
s = read (fd, buf, sizeof (buf));
pthread_cleanup_pop (0);
printf ("%s: read returns with %zd\n", __FUNCTION__, s);
exit (1);
}
@ -165,16 +166,17 @@ tf_readv (void *arg)
exit (1);
}
ssize_t s;
pthread_cleanup_push (cl, NULL);
char buf[100];
struct iovec iov[1] = { [0] = { .iov_base = buf, .iov_len = sizeof (buf) } };
ssize_t s = readv (fd, iov, 1);
printf ("%s: readv returns with %zd\n", __FUNCTION__, s);
s = readv (fd, iov, 1);
pthread_cleanup_pop (0);
printf ("%s: readv returns with %zd\n", __FUNCTION__, s);
exit (1);
}
@ -210,16 +212,17 @@ tf_write (void *arg)
exit (1);
}
ssize_t s;
pthread_cleanup_push (cl, NULL);
char buf[100000];
memset (buf, '\0', sizeof (buf));
ssize_t s = write (fd, buf, sizeof (buf));
printf ("%s: write returns with %zd\n", __FUNCTION__, s);
s = write (fd, buf, sizeof (buf));
pthread_cleanup_pop (0);
printf ("%s: write returns with %zd\n", __FUNCTION__, s);
exit (1);
}
@ -255,17 +258,18 @@ tf_writev (void *arg)
exit (1);
}
ssize_t s;
pthread_cleanup_push (cl, NULL);
char buf[100000];
memset (buf, '\0', sizeof (buf));
struct iovec iov[1] = { [0] = { .iov_base = buf, .iov_len = sizeof (buf) } };
ssize_t s = writev (fd, iov, 1);
printf ("%s: writev returns with %zd\n", __FUNCTION__, s);
s = writev (fd, iov, 1);
pthread_cleanup_pop (0);
printf ("%s: writev returns with %zd\n", __FUNCTION__, s);
exit (1);
}
@ -357,8 +361,7 @@ tf_nanosleep (void *arg)
pthread_cleanup_push (cl, NULL);
struct timespec ts = { .tv_sec = arg == NULL ? 10000000 : 0, .tv_nsec = 0 };
while (nanosleep (&ts, &ts) != 0)
continue;
TEMP_FAILURE_RETRY (nanosleep (&ts, &ts));
pthread_cleanup_pop (0);
@ -403,15 +406,16 @@ tf_select (void *arg)
FD_ZERO (&rfs);
FD_SET (fd, &rfs);
int s;
pthread_cleanup_push (cl, NULL);
int s = select (fd + 1, &rfs, NULL, NULL, NULL);
s = select (fd + 1, &rfs, NULL, NULL, NULL);
pthread_cleanup_pop (0);
printf ("%s: select returns with %d (%s)\n", __FUNCTION__, s,
strerror (errno));
pthread_cleanup_pop (0);
exit (1);
}
@ -451,15 +455,16 @@ tf_pselect (void *arg)
FD_ZERO (&rfs);
FD_SET (fd, &rfs);
int s;
pthread_cleanup_push (cl, NULL);
int s = pselect (fd + 1, &rfs, NULL, NULL, NULL, NULL);
s = pselect (fd + 1, &rfs, NULL, NULL, NULL, NULL);
pthread_cleanup_pop (0);
printf ("%s: pselect returns with %d (%s)\n", __FUNCTION__, s,
strerror (errno));
pthread_cleanup_pop (0);
exit (1);
}
@ -497,15 +502,16 @@ tf_poll (void *arg)
struct pollfd rfs[1] = { [0] = { .fd = fd, .events = POLLIN } };
int s;
pthread_cleanup_push (cl, NULL);
int s = poll (rfs, 1, -1);
s = poll (rfs, 1, -1);
pthread_cleanup_pop (0);
printf ("%s: poll returns with %d (%s)\n", __FUNCTION__, s,
strerror (errno));
pthread_cleanup_pop (0);
exit (1);
}
@ -550,15 +556,16 @@ tf_wait (void *arg)
exit (1);
}
int s;
pthread_cleanup_push (cl, NULL);
int s = wait (NULL);
s = wait (NULL);
pthread_cleanup_pop (0);
printf ("%s: wait returns with %d (%s)\n", __FUNCTION__, s,
strerror (errno));
pthread_cleanup_pop (0);
exit (1);
}
@ -604,15 +611,16 @@ tf_waitpid (void *arg)
exit (1);
}
int s;
pthread_cleanup_push (cl, NULL);
int s = waitpid (-1, NULL, 0);
s = waitpid (-1, NULL, 0);
pthread_cleanup_pop (0);
printf ("%s: waitpid returns with %d (%s)\n", __FUNCTION__, s,
strerror (errno));
pthread_cleanup_pop (0);
exit (1);
}
@ -657,16 +665,17 @@ tf_waitid (void *arg)
exit (1);
}
int s;
pthread_cleanup_push (cl, NULL);
siginfo_t si;
int s = waitid (P_PID, pid, &si, 0);
s = waitid (P_PID, pid, &si, 0);
pthread_cleanup_pop (0);
printf ("%s: waitid returns with %d (%s)\n", __FUNCTION__, s,
strerror (errno));
pthread_cleanup_pop (0);
exit (1);
}
@ -760,8 +769,6 @@ tf_sigwait (void *arg)
}
}
pthread_cleanup_push (cl, NULL);
/* Block SIGUSR1. */
sigset_t mask;
sigaddset (&mask, SIGUSR1);
@ -771,14 +778,16 @@ tf_sigwait (void *arg)
exit (1);
}
/* Wait for SIGUSR1. */
int sig;
pthread_cleanup_push (cl, NULL);
/* Wait for SIGUSR1. */
sigwait (&mask, &sig);
printf ("%s: sigwait returned with signal %d\n", __FUNCTION__, sig);
pthread_cleanup_pop (0);
printf ("%s: sigwait returned with signal %d\n", __FUNCTION__, sig);
exit (1);
}
@ -803,8 +812,6 @@ tf_sigwaitinfo (void *arg)
}
}
pthread_cleanup_push (cl, NULL);
/* Block SIGUSR1. */
sigset_t mask;
sigaddset (&mask, SIGUSR1);
@ -814,15 +821,17 @@ tf_sigwaitinfo (void *arg)
exit (1);
}
/* Wait for SIGUSR1. */
siginfo_t info;
pthread_cleanup_push (cl, NULL);
/* Wait for SIGUSR1. */
sigwaitinfo (&mask, &info);
pthread_cleanup_pop (0);
printf ("%s: sigwaitinfo returned with signal %d\n", __FUNCTION__,
info.si_signo);
pthread_cleanup_pop (0);
exit (1);
}
@ -847,8 +856,6 @@ tf_sigtimedwait (void *arg)
}
}
pthread_cleanup_push (cl, NULL);
/* Block SIGUSR1. */
sigset_t mask;
sigaddset (&mask, SIGUSR1);
@ -861,13 +868,15 @@ tf_sigtimedwait (void *arg)
/* Wait for SIGUSR1. */
siginfo_t info;
struct timespec ts = { .tv_sec = 60, .tv_nsec = 0 };
pthread_cleanup_push (cl, NULL);
sigtimedwait (&mask, &info, &ts);
pthread_cleanup_pop (0);
printf ("%s: sigtimedwait returned with signal %d\n", __FUNCTION__,
info.si_signo);
pthread_cleanup_pop (0);
exit (1);
}
@ -927,7 +936,7 @@ tf_accept (void *arg)
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-XXXXXX");
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-1-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
@ -995,7 +1004,7 @@ tf_send (void *arg)
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-XXXXXX");
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-2-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
@ -1077,7 +1086,7 @@ tf_recv (void *arg)
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-XXXXXX");
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-3-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
@ -1158,7 +1167,7 @@ tf_recvfrom (void *arg)
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-XXXXXX");
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-4-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
@ -1233,7 +1242,7 @@ tf_recvmsg (void *arg)
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-XXXXXX");
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-5-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
@ -1548,6 +1557,399 @@ tf_msync (void *arg)
}
static void *
tf_sendto (void *arg)
{
if (arg == NULL)
// XXX If somebody can provide a portable test case in which sendto()
// blocks we can enable this test to run in both rounds.
abort ();
struct sockaddr_un sun;
tempfd = socket (AF_UNIX, SOCK_DGRAM, 0);
if (tempfd == -1)
{
printf ("%s: first socket call failed\n", __FUNCTION__);
exit (1);
}
int tries = 0;
do
{
if (++tries > 10)
{
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-6-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
exit (1);
}
sun.sun_family = AF_UNIX;
}
while (bind (tempfd, (struct sockaddr *) &sun,
offsetof (struct sockaddr_un, sun_path)
+ strlen (sun.sun_path) + 1) != 0);
tempfname = strdup (sun.sun_path);
tempfd2 = socket (AF_UNIX, SOCK_DGRAM, 0);
if (tempfd2 == -1)
{
printf ("%s: second socket call failed\n", __FUNCTION__);
exit (1);
}
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
pthread_cleanup_push (cl, NULL);
char mem[1];
sendto (tempfd2, mem, arg == NULL ? sizeof (mem) : 1, 0,
(struct sockaddr *) &sun,
offsetof (struct sockaddr_un, sun_path) + strlen (sun.sun_path) + 1);
pthread_cleanup_pop (0);
printf ("%s: sendto returned\n", __FUNCTION__);
exit (1);
}
static void *
tf_sendmsg (void *arg)
{
if (arg == NULL)
// XXX If somebody can provide a portable test case in which sendmsg()
// blocks we can enable this test to run in both rounds.
abort ();
struct sockaddr_un sun;
tempfd = socket (AF_UNIX, SOCK_DGRAM, 0);
if (tempfd == -1)
{
printf ("%s: first socket call failed\n", __FUNCTION__);
exit (1);
}
int tries = 0;
do
{
if (++tries > 10)
{
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-7-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
exit (1);
}
sun.sun_family = AF_UNIX;
}
while (bind (tempfd, (struct sockaddr *) &sun,
offsetof (struct sockaddr_un, sun_path)
+ strlen (sun.sun_path) + 1) != 0);
tempfname = strdup (sun.sun_path);
tempfd2 = socket (AF_UNIX, SOCK_DGRAM, 0);
if (tempfd2 == -1)
{
printf ("%s: second socket call failed\n", __FUNCTION__);
exit (1);
}
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
pthread_cleanup_push (cl, NULL);
char mem[1];
struct iovec iov[1];
iov[0].iov_base = mem;
iov[0].iov_len = 1;
struct msghdr m;
m.msg_name = &sun;
m.msg_namelen = (offsetof (struct sockaddr_un, sun_path)
+ strlen (sun.sun_path) + 1);
m.msg_iov = iov;
m.msg_iovlen = 1;
m.msg_control = NULL;
m.msg_controllen = 0;
sendmsg (tempfd2, &m, 0);
pthread_cleanup_pop (0);
printf ("%s: sendmsg returned\n", __FUNCTION__);
exit (1);
}
static void *
tf_creat (void *arg)
{
if (arg == NULL)
// XXX If somebody can provide a portable test case in which sendmsg()
// blocks we can enable this test to run in both rounds.
abort ();
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
pthread_cleanup_push (cl, NULL);
creat ("tmp/tst-cancel-4-should-not-exist", 0666);
pthread_cleanup_pop (0);
printf ("%s: creat returned\n", __FUNCTION__);
exit (1);
}
static void *
tf_connect (void *arg)
{
if (arg == NULL)
// XXX If somebody can provide a portable test case in which connect()
// blocks we can enable this test to run in both rounds.
abort ();
struct sockaddr_un sun;
tempfd = socket (AF_UNIX, SOCK_STREAM, 0);
if (tempfd == -1)
{
printf ("%s: first socket call failed\n", __FUNCTION__);
exit (1);
}
int tries = 0;
do
{
if (++tries > 10)
{
printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__);
}
strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-2-XXXXXX");
if (mktemp (sun.sun_path) == NULL)
{
printf ("%s: cannot generate temp file name\n", __FUNCTION__);
exit (1);
}
sun.sun_family = AF_UNIX;
}
while (bind (tempfd, (struct sockaddr *) &sun,
offsetof (struct sockaddr_un, sun_path)
+ strlen (sun.sun_path) + 1) != 0);
tempfname = strdup (sun.sun_path);
listen (tempfd, 5);
tempfd2 = socket (AF_UNIX, SOCK_STREAM, 0);
if (tempfd2 == -1)
{
printf ("%s: second socket call failed\n", __FUNCTION__);
exit (1);
}
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
if (arg != NULL)
{
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
}
pthread_cleanup_push (cl, NULL);
connect (tempfd2, (struct sockaddr *) &sun, sizeof (sun));
pthread_cleanup_pop (0);
printf ("%s: connect returned\n", __FUNCTION__);
exit (1);
}
static void *
tf_tcdrain (void *arg)
{
if (arg == NULL)
// XXX If somebody can provide a portable test case in which tcdrain()
// blocks we can enable this test to run in both rounds.
abort ();
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
if (arg != NULL)
{
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
}
pthread_cleanup_push (cl, NULL);
/* Regardless of stderr being a terminal, the tcdrain call should be
canceled. */
tcdrain (STDERR_FILENO);
pthread_cleanup_pop (0);
printf ("%s: tcdrain returned\n", __FUNCTION__);
exit (1);
}
static void *
tf_msgrcv (void *arg)
{
tempmsg = msgget (random (), 0666 | IPC_CREAT);
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
if (arg != NULL)
{
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
}
pthread_cleanup_push (cl, NULL);
struct
{
long int type;
char mem[10];
} m;
msgrcv (tempmsg, (struct msgbuf *) &m, 10, 100, 0);
pthread_cleanup_pop (0);
printf ("%s: msgrcv returned\n", __FUNCTION__);
exit (1);
}
static void *
tf_msgsnd (void *arg)
{
if (arg == NULL)
// XXX If somebody can provide a portable test case in which msgsnd()
// blocks we can enable this test to run in both rounds.
abort ();
tempmsg = msgget (random (), 0666 | IPC_CREAT);
int r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: barrier_wait failed\n", __FUNCTION__);
exit (1);
}
r = pthread_barrier_wait (&b2);
if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
{
printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
exit (1);
}
pthread_cleanup_push (cl, NULL);
struct
{
long int type;
char mem[1];
} m;
m.type = 100;
msgsnd (tempmsg, (struct msgbuf *) &m, sizeof (m.mem), 0);
pthread_cleanup_pop (0);
printf ("%s: msgsnd returned\n", __FUNCTION__);
exit (1);
}
static struct
{
const char *name;
@ -1587,6 +1989,13 @@ static struct
ADD_TEST (pwrite, 2, 1),
ADD_TEST (fsync, 2, 1),
ADD_TEST (msync, 2, 1),
ADD_TEST (sendto, 2, 1),
ADD_TEST (sendmsg, 2, 1),
ADD_TEST (creat, 2, 1),
ADD_TEST (connect, 2, 1),
ADD_TEST (tcdrain, 2, 1),
ADD_TEST (msgrcv, 2, 0),
ADD_TEST (msgsnd, 2, 1),
};
#define ntest_tf (sizeof (tests) / sizeof (tests[0]))
@ -1783,8 +2192,17 @@ do_test (void)
close (tempfd2);
tempfd2 = -1;
}
free (tempfname);
tempfname = NULL;
if (tempfname != NULL)
{
unlink (tempfname);
free (tempfname);
tempfname = NULL;
}
if (tempmsg != -1)
{
msgctl (tempmsg, IPC_RMID, NULL);
tempmsg = -1;
}
}
return result;

1
nptl/tst-cancelx18.c Normal file
View File

@ -0,0 +1 @@
#include "tst-cancel18.c"

View File

@ -3341,7 +3341,7 @@ build_word_op (dfa, trans, not, err)
#ifdef RE_ENABLE_I18N
mbcset, &alloc,
#endif /* RE_ENABLE_I18N */
(const unsigned char *) "alpha", 0);
(const unsigned char *) "alnum", 0);
if (BE (ret != REG_NOERROR, 0))
{