From 026148e2e90368c0b23192f506e15aa6197105aa Mon Sep 17 00:00:00 2001 From: Owen Shepherd Date: Thu, 8 Nov 2012 23:17:29 +0000 Subject: [PATCH] PDCLIB-15 PDCLIB-16: * Move _PDCLIB_file_t to _PDCLIB_io.h, compertmentalizing dependencies. Removes I/O specific structures from _PDCLIB_int.h. Allows _PDCLIB_io.h to depend upon _PDCLIB_threadconfig.h without recursive dependencies happening. * Add "lock" member to _PDCLIB_file_t as the stream locking mutex * Add flockfile/ftrylockfile/funlockfile as functions which manipulate the locking mutex explicitly * Add man page on flockfile/funlockfile/ftrylockfile * Add flockfile/ftrylockfile/funlockfile and *_unlocked to and as appropriate * win32: Initialize mutex member correctly --- functions/_PDCLIB/filemode.c | 1 + functions/stdio/flockfile.c | 32 ++++++++++ functions/stdio/ftrylockfile.c | 39 ++++++++++++ functions/stdio/funlockfile.c | 37 +++++++++++ functions/wchar/wcstok.c | 4 ++ includes/stdio.h | 46 +++++++++++++- includes/wchar.h | 13 +++- internals/_PDCLIB_aux.h | 55 ++++++++++++++-- internals/_PDCLIB_int.h | 53 ---------------- internals/_PDCLIB_io.h | 63 +++++++++++++++++++ man3/flockfile.3 | 56 +++++++++++++++++ platform/win32/crt0.c | 9 +++ .../win32/functions/_PDCLIB/_PDCLIB_stdinit.c | 6 +- 13 files changed, 352 insertions(+), 62 deletions(-) create mode 100644 functions/stdio/flockfile.c create mode 100644 functions/stdio/ftrylockfile.c create mode 100644 functions/stdio/funlockfile.c create mode 100644 internals/_PDCLIB_io.h create mode 100644 man3/flockfile.3 diff --git a/functions/_PDCLIB/filemode.c b/functions/_PDCLIB/filemode.c index 4548106..c6066f8 100644 --- a/functions/_PDCLIB/filemode.c +++ b/functions/_PDCLIB/filemode.c @@ -9,6 +9,7 @@ #include #ifndef REGTEST +#include <_PDCLIB_io.h> /* Helper function that parses the C-style mode string passed to fopen() into the PDCLib flags FREAD, FWRITE, FAPPEND, FRW (read-write) and FBIN (binary mode). diff --git a/functions/stdio/flockfile.c b/functions/stdio/flockfile.c new file mode 100644 index 0000000..4e80daf --- /dev/null +++ b/functions/stdio/flockfile.c @@ -0,0 +1,32 @@ +/* flockfile(FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +#ifndef REGTEST +#include +#include + +void flockfile( FILE * file ) +{ + if( mtx_lock( &file->lock ) != thrd_success ) { + abort(); + } +} + +#endif + +#ifdef TEST +#include <_PDCLIB_test.h> + +int main( void ) +{ + // Not tested here - tested by other stdio test drivers + return TEST_RESULTS; +} + +#endif diff --git a/functions/stdio/ftrylockfile.c b/functions/stdio/ftrylockfile.c new file mode 100644 index 0000000..cb8147a --- /dev/null +++ b/functions/stdio/ftrylockfile.c @@ -0,0 +1,39 @@ +/* ftrylockfile(FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +#ifndef REGTEST +#include +#include + +int ftrylockfile( FILE * file ) +{ + int res = mtx_trylock( &file->lock ); + switch(res) { + case thrd_success: + return 0; + case thrd_busy: + return 1; + + default: + abort(); + } +} + +#endif + +#ifdef TEST +#include <_PDCLIB_test.h> + +int main( void ) +{ + // Not tested here - tested by other stdio test drivers + return TEST_RESULTS; +} + +#endif diff --git a/functions/stdio/funlockfile.c b/functions/stdio/funlockfile.c new file mode 100644 index 0000000..0845aab --- /dev/null +++ b/functions/stdio/funlockfile.c @@ -0,0 +1,37 @@ +/* funlockfile(FILE * ) + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +#include +#include + +#ifndef REGTEST +#include +#include + +void funlockfile( FILE * file ) +{ + int res = mtx_unlock( &file->lock ); + switch(res) { + case thrd_success: + return; + + default: + abort(); + } +} + +#endif + +#ifdef TEST +#include <_PDCLIB_test.h> + +int main( void ) +{ + // Not tested here - tested by other stdio test drivers + return TEST_RESULTS; +} + +#endif diff --git a/functions/wchar/wcstok.c b/functions/wchar/wcstok.c index 62f8dd8..6bd3bca 100644 --- a/functions/wchar/wcstok.c +++ b/functions/wchar/wcstok.c @@ -78,6 +78,9 @@ wchar_t * wcstok( wchar_t * _PDCLIB_restrict s1, int main( void ) { + // MinGW at least has a very nonconforming (different signature!) variety + // of wcstok +#ifndef REGTEST wchar_t s[] = L"_a_bc__d_"; wchar_t* state = NULL; wchar_t* tokres; @@ -104,6 +107,7 @@ int main( void ) TESTCASE( s[4] == L'd' ); TESTCASE( s[5] == L'\0' ); TESTCASE( ( tokres = wcstok( NULL, L"_", &state ) ) == NULL ); +#endif return TEST_RESULTS; } #endif diff --git a/includes/stdio.h b/includes/stdio.h index 7441333..acc0012 100644 --- a/includes/stdio.h +++ b/includes/stdio.h @@ -8,7 +8,7 @@ #ifndef _PDCLIB_STDIO_H #define _PDCLIB_STDIO_H _PDCLIB_STDIO_H -#include <_PDCLIB_int.h> +#include <_PDCLIB_io.h> _PDCLIB_BEGIN_EXTERN_C #ifndef _PDCLIB_SIZE_T_DEFINED @@ -817,5 +817,49 @@ int ferror( FILE * stream ) _PDCLIB_nothrow; */ void perror( const char * s ) _PDCLIB_nothrow; +/* Unlocked I/O + * + * Since threading was introduced in C11, FILE objects have had implicit locks + * to prevent data races and inconsistent output. + * + * PDCLib provides these functions from POSIX as an extension in order to enable + * users to access the underlying unlocked functions. + * + * For each function defined in C11 where an _unlocked variant is defined below, + * the behaviour of the _unlocked variant is the same except that it will not + * take the lock associated with the stream. + * + * flockfile, ftrylockfile and funlockfile can be used to manually manipulate + * the stream locks. The behaviour of the _unlocked functions if called when the + * stream isn't locked by the calling thread is implementation defined. + */ +#if _PDCLIB_POSIX_MIN(200112L) || _PDCLIB_BSD_SOURCE || _PDCLIB_SVID_SOURCE +void flockfile(FILE *file); +int ftrylockfile(FILE *file); +void funlockfile(FILE *file); + +int getc_unlocked(FILE *stream); +int getchar_unlocked(void); +int putc_unlocked(int c, FILE *stream); +int putchar_unlocked(int c); +#endif + +#if _PDCLIB_BSD_SOURCE || _PDCLIB_SVID_SOURCE +void clearerr_unlocked(FILE *stream); +int feof_unlocked(FILE *stream); +int ferror_unlocked(FILE *stream); +int fileno_unlocked(FILE *stream); +int fflush_unlocked(FILE *stream); +int fgetc_unlocked(FILE *stream); +int fputc_unlocked(int c, FILE *stream); +size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *stream); +size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *stream); +#endif + +#if _PDCLIB_GNU_SOURCE +char *fgets_unlocked(char *s, int n, FILE *stream); +int fputs_unlocked(const char *s, FILE *stream); +#endif + _PDCLIB_END_EXTERN_C #endif diff --git a/includes/wchar.h b/includes/wchar.h index bac7f40..34f2ef3 100644 --- a/includes/wchar.h +++ b/includes/wchar.h @@ -7,7 +7,7 @@ #ifndef _PDCLIB_WCHAR_H #define _PDCLIB_WCHAR_H -#include <_PDCLIB_int.h> +#include <_PDCLIB_io.h> _PDCLIB_BEGIN_EXTERN_C /* This is VASTLY incomplete. Functions being implemented as required by other portions of the library @@ -101,6 +101,17 @@ wint_t putwc(wchar_t c, struct _PDCLIB_file_t *stream); wint_t putwchar(wchar_t c); wint_t ungetwc(wint_t c, struct _PDCLIB_file_t *stream); +#if _PDCLIB_GNU_SOURCE +wint_t getwc_unlocked(struct _PDCLIB_file_t *stream); +wint_t getwchar_unlocked(void); +wint_t fgetwc_unlocked(struct _PDCLIB_file_t *stream); +wint_t fputwc_unlocked(wchar_t wc, struct _PDCLIB_file_t *stream); +wint_t putwc_unlocked(wchar_t wc, struct _PDCLIB_file_t *stream); +wint_t putwchar_unlocked(wchar_t wc); +wchar_t *fgetws_unlocked(wchar_t *ws, int n, struct _PDCLIB_file_t *stream); +int fputws_unlocked(const wchar_t *ws, struct _PDCLIB_file_t *stream); +#endif + /* Wide character <-> Numeric conversions */ #if 0 double wcstod(const wchar_t *_PDCLIB_restrict nptr, wchar_t **_PDCLIB_restrict endptr); diff --git a/internals/_PDCLIB_aux.h b/internals/_PDCLIB_aux.h index 3de8d4a..bfb0842 100644 --- a/internals/_PDCLIB_aux.h +++ b/internals/_PDCLIB_aux.h @@ -200,10 +200,21 @@ * XOPEN: X/Open System Interface (XSI)/Single Unix Specification * 0 (XPG4), 500 (SUSv2/UNIX98), 600 (SUSv3/UNIX03), 700 (SUSv4) * - * PDCLib does not attempt or claim POSIX comformance, but makes available these - * extensions as - * (a) useful, and - * (b) + * Additionally, the macros + * _BSD_SOURCE, _SVID_SOURCE and _GNU_SOURCE + * are adhered to. If _GNU_SOURCE is defined, _XOPEN_SOURCE and + * _POSIX_C_SOURCE are defined to their most recent values to match glibc + * behaviour + * + * The intention of supporting these feature test macros is to ease + * application portability from these systems to PDCLib systems; in addition, + * it eases support for these standards by systems supporting them which are + * using PDCLib as their default C library. + * + * Applications targetting purely PDClib/PDCLib based platforms may define + * just _PDCLIB_EXTENSIONS, which will enable all supported extensions, plus + * all features from all supported versions of C and C++. + * */ #define _PDCLIB_C_MIN(min) _PDCLIB_C_MINMAX(min, 3000) #define _PDCLIB_CXX_MIN(min) _PDCLIB_CXX_MINMAX(min, 3000) @@ -218,6 +229,16 @@ #define _PDCLIB_CXX_MINMAX(min, max) 1 #define _PDCLIB_POSIX_MINMAX(min, max) 1 #define _PDCLIB_XOPEN_MINMAX(min, max) 1 + + #undef _PDCLIB_EXTENSIONS + #undef _PDCLIB_BSD_SOURCE + #undef _PDCLIB_SVID_SOURCE + #undef _PDCLIB_GNU_SOURCE + + #define _PDCLIB_EXTENSIONS 1 + #define _PDCLIB_BSD_SOURCE 1 + #define _PDCLIB_SVID_SOURCE 1 + #define _PDCLIB_GNU_SOURCE 1 #else #define _PDCLIB_C_MINMAX(min, max) \ (_PDCLIB_C_VERSION >= (min) && _PDCLIB_C_VERSION <= (max)) @@ -236,6 +257,32 @@ #define _XOPEN_SOURCE 0 #endif + #if defined(_GNU_SOURCE) + #define _PDCLIB_GNU_SOURCE 1 + #define _PDCLIB_SVID_SOURCE 1 + #define _PDCLIB_BSD_SOURCE 1 + #undef _XOPEN_SOURCE + #define _XOPEN_SOURCE 700 + #else + #define _PDCLIB_GNU_SOURCE 0 + #endif + + #if defined(_PDCLIB_BSD_SOURCE) + // pass + #elif defined(_BSD_SOURCE) + #define _PDCLIB_BSD_SOURCE 1 + #else + #define _PDCLIB_BSD_SOURCE 0 + #endif + + #if defined(_PDCLIB_SVID_SOURCE) + // pass + #elif defined(_SVID_SOURCE) + #define _PDCLIB_SVID_SOURCE 1 + #else + #define _PDCLIB_SVID_SOURCE 0 + #endif + #if _PDCLIB_XOPEN_MIN(700) && !_PDCLIB_POSIX_MIN(200809L) #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 2008098L diff --git a/internals/_PDCLIB_int.h b/internals/_PDCLIB_int.h index 2900ff3..da20dfe 100644 --- a/internals/_PDCLIB_int.h +++ b/internals/_PDCLIB_int.h @@ -278,59 +278,6 @@ typedef _PDCLIB_clock _PDCLIB_clock_t; int tm_isdst; \ }; #endif - -/* -------------------------------------------------------------------------- */ -/* Various internals */ -/* -------------------------------------------------------------------------- */ - -/* Flags for representing mode (see fopen()). Note these must fit the same - status field as the _IO?BF flags in and the internal flags below. -*/ -#define _PDCLIB_FREAD 8u -#define _PDCLIB_FWRITE 16u -#define _PDCLIB_FAPPEND 32u -#define _PDCLIB_FRW 64u -#define _PDCLIB_FBIN 128u - -/* Internal flags, made to fit the same status field as the flags above. */ -/* -------------------------------------------------------------------------- */ -/* free() the buffer memory on closing (false for user-supplied buffer) */ -#define _PDCLIB_FREEBUFFER 512u -/* stream has encountered error / EOF */ -#define _PDCLIB_ERRORFLAG 1024u -#define _PDCLIB_EOFFLAG 2048u -/* stream is wide-oriented */ -#define _PDCLIB_WIDESTREAM 4096u -/* stream is byte-oriented */ -#define _PDCLIB_BYTESTREAM 8192u -/* file associated with stream should be remove()d on closing (tmpfile()) */ -#define _PDCLIB_DELONCLOSE 16384u -/* stream handle should not be free()d on close (stdin, stdout, stderr) */ -#define _PDCLIB_STATIC 32768u - -/* Position / status structure for getpos() / fsetpos(). */ -struct _PDCLIB_fpos_t -{ - _PDCLIB_uint64_t offset; /* File position offset */ - int status; /* Multibyte parsing state (unused, reserved) */ -}; - -/* FILE structure */ -struct _PDCLIB_file_t -{ - _PDCLIB_fd_t handle; /* OS file handle */ - char * buffer; /* Pointer to buffer memory */ - _PDCLIB_size_t bufsize; /* Size of buffer */ - _PDCLIB_size_t bufidx; /* Index of current position in buffer */ - _PDCLIB_size_t bufend; /* Index of last pre-read character in buffer */ - struct _PDCLIB_fpos_t pos; /* Offset and multibyte parsing state */ - _PDCLIB_size_t ungetidx; /* Number of ungetc()'ed characters */ - unsigned char * ungetbuf; /* ungetc() buffer */ - unsigned int status; /* Status flags; see above */ - /* multibyte parsing status to be added later */ - char * filename; /* Name the current stream has been opened with */ - struct _PDCLIB_file_t * next; /* Pointer to next struct (internal) */ -}; /* -------------------------------------------------------------------------- */ /* Internal data types */ diff --git a/internals/_PDCLIB_io.h b/internals/_PDCLIB_io.h new file mode 100644 index 0000000..0dfdad2 --- /dev/null +++ b/internals/_PDCLIB_io.h @@ -0,0 +1,63 @@ +#ifndef _PDCLIB_IO_H +#define _PDCLIB_IO_H +#include "_PDCLIB_int.h" +#include "_PDCLIB_threadconfig.h" + +/* PDCLib internal I/O logic <_PDCLIB_int.h> + + This file is part of the Public Domain C Library (PDCLib). + Permission is granted to use, modify, and / or redistribute at will. +*/ + +/* Flags for representing mode (see fopen()). Note these must fit the same + status field as the _IO?BF flags in and the internal flags below. +*/ +#define _PDCLIB_FREAD 8u +#define _PDCLIB_FWRITE 16u +#define _PDCLIB_FAPPEND 32u +#define _PDCLIB_FRW 64u +#define _PDCLIB_FBIN 128u + +/* Internal flags, made to fit the same status field as the flags above. */ +/* -------------------------------------------------------------------------- */ +/* free() the buffer memory on closing (false for user-supplied buffer) */ +#define _PDCLIB_FREEBUFFER 512u +/* stream has encountered error / EOF */ +#define _PDCLIB_ERRORFLAG 1024u +#define _PDCLIB_EOFFLAG 2048u +/* stream is wide-oriented */ +#define _PDCLIB_WIDESTREAM 4096u +/* stream is byte-oriented */ +#define _PDCLIB_BYTESTREAM 8192u +/* file associated with stream should be remove()d on closing (tmpfile()) */ +#define _PDCLIB_DELONCLOSE 16384u +/* stream handle should not be free()d on close (stdin, stdout, stderr) */ +#define _PDCLIB_STATIC 32768u + +/* Position / status structure for getpos() / fsetpos(). */ +struct _PDCLIB_fpos_t +{ + _PDCLIB_uint64_t offset; /* File position offset */ + int status; /* Multibyte parsing state (unused, reserved) */ +}; + +/* FILE structure */ +struct _PDCLIB_file_t +{ + _PDCLIB_fd_t handle; /* OS file handle */ + _PDCLIB_MTX_T lock; /* file lock */ + char * buffer; /* Pointer to buffer memory */ + _PDCLIB_size_t bufsize; /* Size of buffer */ + _PDCLIB_size_t bufidx; /* Index of current position in buffer */ + _PDCLIB_size_t bufend; /* Index of last pre-read character in buffer */ + struct _PDCLIB_fpos_t pos; /* Offset and multibyte parsing state */ + _PDCLIB_size_t ungetidx; /* Number of ungetc()'ed characters */ + unsigned char * ungetbuf; /* ungetc() buffer */ + unsigned int status; /* Status flags; see above */ + /* multibyte parsing status to be added later */ + char * filename; /* Name the current stream has been opened with */ + struct _PDCLIB_file_t * next; /* Pointer to next struct (internal) */ +}; + + +#endif diff --git a/man3/flockfile.3 b/man3/flockfile.3 new file mode 100644 index 0000000..a8a4a3b --- /dev/null +++ b/man3/flockfile.3 @@ -0,0 +1,56 @@ +.\" This file is part of the Public Domain C Library (PDCLib). +.\" Permission is granted to use, modify, and / or redistribute at will. +.\" +.Dd +.Dt flockfile 3 +.Os + +.Sh NAME +.Nm flockfile, ftrylockfile, funlockfile +.Nd stdio file locking + +.Sh SYNOPSIS +.Sy #define _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || _SVID_SOURCE || _BSD_SOURCE + +.In stdio.h +.Fn "void flockfile" "FILE *file" +.Fn "int ftrylockfile" "FILE *file" +.Fn "void funlockfile" "FILE *file" + +.Sh DESCRIPTION +.Fn flockfile +locks the passed FILE stream for access by the calling thread, potentially +blocking. +.Fn ftrylockfile +attempts to lock the file for the calling thread, but will return failure if +another thread has already locked the file. +.Fn funlockfile +releases the lock on the stream, allowing another thread in the process to +access it. +.\" +.Pp +The same stream may be locked multiple times by the calling thread; the number +of calls to +.Fn flockfile +and +.Fn ftrylockfile +on a file must be equal to the number of calls to +.Fn funlockfile . +.\" +.Pp +No other thread may do I/O through the locked file while it is locked. + +.Sh IMPLEMENTATION NOTES +PDCLib implements the file locking on top of the Mutex primitives added by C11 + +.Sh SEE ALSO +.Xr fopen 3 +.Xr fclose 3 +.Xr unlocked_stdio 3 +.Xr mtx_t 3 + +.Sh STANDARDS +The locked I/O routines were initially introduced in +.St -svid4 , +and incorporated into POSIX in +.St -p1003.1-2001 . diff --git a/platform/win32/crt0.c b/platform/win32/crt0.c index 076e76e..7da2a81 100644 --- a/platform/win32/crt0.c +++ b/platform/win32/crt0.c @@ -2,6 +2,7 @@ #include #include #include +#include #include // Watcom bug: winnt.h assumes string.h defines wchar_t #include @@ -112,6 +113,14 @@ void __cdecl mainCRTStartup( void ) cl = GetCommandLineW(); wargv = CommandLineToArgvW(cl, &argc); argv = argvToAnsi(wargv, argc); + + if( mtx_init(&stdin->lock, mtx_recursive) != thrd_success + || mtx_init(&stdout->lock, mtx_recursive) != thrd_success + || mtx_init(&stderr->lock, mtx_recursive) != thrd_success ) { + fputs( "Error during C runtime initialization: " + "Unable to allocate stdio mutex", stderr ); + } + atexit(freeArgs); int exitStatus = main(argc, argv, NULL); diff --git a/platform/win32/functions/_PDCLIB/_PDCLIB_stdinit.c b/platform/win32/functions/_PDCLIB/_PDCLIB_stdinit.c index bc40470..8eb40f7 100644 --- a/platform/win32/functions/_PDCLIB/_PDCLIB_stdinit.c +++ b/platform/win32/functions/_PDCLIB/_PDCLIB_stdinit.c @@ -29,9 +29,9 @@ static unsigned char _PDCLIB_sin_ungetbuf[_PDCLIB_UNGETCBUFSIZE]; static unsigned char _PDCLIB_sout_ungetbuf[_PDCLIB_UNGETCBUFSIZE]; static unsigned char _PDCLIB_serr_ungetbuf[_PDCLIB_UNGETCBUFSIZE]; -static struct _PDCLIB_file_t _PDCLIB_serr = { NULL, _PDCLIB_serr_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_serr_ungetbuf, _IONBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, NULL }; -static struct _PDCLIB_file_t _PDCLIB_sout = { NULL, _PDCLIB_sout_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sout_ungetbuf, _IOLBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, &_PDCLIB_serr }; -static struct _PDCLIB_file_t _PDCLIB_sin = { NULL, _PDCLIB_sin_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sin_ungetbuf, _IOLBF | _PDCLIB_FREAD | _PDCLIB_STATIC, NULL, &_PDCLIB_sout }; +static struct _PDCLIB_file_t _PDCLIB_serr = { NULL, { 0 }, _PDCLIB_serr_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_serr_ungetbuf, _IONBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, NULL }; +static struct _PDCLIB_file_t _PDCLIB_sout = { NULL, { 0 }, _PDCLIB_sout_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sout_ungetbuf, _IOLBF | _PDCLIB_FWRITE | _PDCLIB_STATIC, NULL, &_PDCLIB_serr }; +static struct _PDCLIB_file_t _PDCLIB_sin = { NULL, { 0 }, _PDCLIB_sin_buffer, BUFSIZ, 0, 0, { 0, 0 }, 0, _PDCLIB_sin_ungetbuf, _IOLBF | _PDCLIB_FREAD | _PDCLIB_STATIC, NULL, &_PDCLIB_sout }; struct _PDCLIB_file_t * stdin = &_PDCLIB_sin; struct _PDCLIB_file_t * stdout = &_PDCLIB_sout; -- 2.40.0