From a14bbb9609fd00801c65328838a42db75af486ec Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 31 Mar 2013 18:21:13 +0800 Subject: [PATCH] Usermode - Added stdio output caching --- Usermode/Applications/CLIShell_src/main.c | 1 + Usermode/Applications/login_src/main.c | 3 + .../Libraries/libc.so_src/include_exp/stdio.h | 4 + Usermode/Libraries/libc.so_src/stdio.c | 254 +++++++++++++++--- Usermode/Libraries/libc.so_src/stdio_int.h | 18 +- Usermode/Libraries/libc.so_src/stdlib.c | 28 +- Usermode/Libraries/libc.so_src/stub.c | 20 +- 7 files changed, 266 insertions(+), 62 deletions(-) diff --git a/Usermode/Applications/CLIShell_src/main.c b/Usermode/Applications/CLIShell_src/main.c index 28007e12..ca06a058 100644 --- a/Usermode/Applications/CLIShell_src/main.c +++ b/Usermode/Applications/CLIShell_src/main.c @@ -75,6 +75,7 @@ int main(int argc, char *argv[], char **envp) if(saArgs[0]) free(saArgs[0]); printf("%s$ ", gsCurrentDirectory); + fflush(stdout); // Read Command line sCommandStr = Readline( readline_state ); diff --git a/Usermode/Applications/login_src/main.c b/Usermode/Applications/login_src/main.c index 59b4a784..81bd4ee3 100644 --- a/Usermode/Applications/login_src/main.c +++ b/Usermode/Applications/login_src/main.c @@ -72,6 +72,7 @@ char *GetUsername() // Prompt the user printf("Username: "); + fflush(stdout); // Read in text while( (ch = fgetc(stdin)) != -1 && ch != '\n' ) @@ -86,6 +87,7 @@ char *GetUsername() // Echo out to the screen fputc(ch, stdout); + fflush(stdout); if(pos == BUFLEN-1) break; } @@ -108,6 +110,7 @@ char *GetPassword() // Prompt the user printf("Password: "); + fflush(stdout); // Read in text while( (ch = fgetc(stdin)) != -1 && ch != '\n' ) diff --git a/Usermode/Libraries/libc.so_src/include_exp/stdio.h b/Usermode/Libraries/libc.so_src/include_exp/stdio.h index b3836524..70e71388 100644 --- a/Usermode/Libraries/libc.so_src/include_exp/stdio.h +++ b/Usermode/Libraries/libc.so_src/include_exp/stdio.h @@ -22,6 +22,10 @@ typedef struct sFILE FILE; #define SEEK_END (-1) #endif +#define _IOFBF 2 +#define _IOLBF 1 +#define _IONBF 0 + #ifdef ARCHDIR_is_native #define printf acess_printf #define vsprintf acess_vsprintf diff --git a/Usermode/Libraries/libc.so_src/stdio.c b/Usermode/Libraries/libc.so_src/stdio.c index c2b405c1..f71df600 100644 --- a/Usermode/Libraries/libc.so_src/stdio.c +++ b/Usermode/Libraries/libc.so_src/stdio.c @@ -9,8 +9,8 @@ #include #include "lib.h" #include "stdio_int.h" - -#define WRITE_STR(_fd, _str) write(_fd, _str, sizeof(_str)) +#include +#include #define DEBUG_BUILD 0 @@ -19,7 +19,6 @@ #define _stdout 1 // === PROTOTYPES === -EXPORT void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned); struct sFILE *get_file_struct(); // === GLOBALS === @@ -28,8 +27,39 @@ struct sFILE *stdin; // Standard Input struct sFILE *stdout; // Standard Output struct sFILE *stderr; // Standard Error ///\note Initialised in SoMain +static const int STDIN_BUFSIZ = 512; +static const int STDOUT_BUFSIZ = 512; // === CODE === +void _stdio_init(void) +{ + // Init FileIO Pointers + stdin = &_iob[0]; + stdin->FD = 0; + stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED; + stdin->Buffer = malloc(STDIN_BUFSIZ); + stdin->BufferSpace = STDIN_BUFSIZ; + + stdout = &_iob[1]; + stdout->FD = 1; + stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED; + stdout->Buffer = malloc(STDOUT_BUFSIZ); + stdout->BufferSpace = STDOUT_BUFSIZ; + + stderr = &_iob[2]; + stderr->FD = 2; + stderr->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE; +} + +void _stdio_cleanup(void) +{ + int i; + for( i = 0; i < STDIO_MAX_STREAMS; i ++ ) + { + fclose( &_iob[i] ); + } +} + int _fopen_modetoflags(const char *mode) { int flags = 0; @@ -158,16 +188,18 @@ EXPORT FILE *fmemopen(void *buffer, size_t length, const char *mode) } ret->Buffer = buffer; - ret->BufferStart = 0; - ret->BufferSize = length; + ret->BufferPos = 0; + ret->BufferSpace = length; return ret; } EXPORT int fclose(FILE *fp) { + if( !(fp->Flags & FILE_FLAG_ALLOC) ) + return 0; fflush(fp); - if( fp->FD != -1 ) { + if( fp->FD >= 0 ) { _SysClose(fp->FD); } fp->Flags = 0; @@ -175,17 +207,110 @@ EXPORT int fclose(FILE *fp) return 0; } +EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size) +{ + if( !fp ) { + errno = EINVAL; + return 1; + } + + // Check for memory files + if( fp->FD == -2 ) { + errno = EINVAL; + return 2; + } + + // Not strictly needed, as this should only be called before any IO + fflush(fp); + + // Eliminate any pre-existing buffer + if( fp->Buffer ) { + free( fp->Buffer ); + fp->Buffer = NULL; + fp->BufferSpace = 0; + fp->BufferPos = 0; + } + + if( mode == _IONBF ) { + // Do nothing, buffering was disabled above + } + else + { + // Sanity check buffering mode before allocating + if( mode != _IOLBF && mode != _IOFBF ) { + errno = EINVAL; + return 1; + } + // Allocate a buffer if one was not provided + if( !buffer ) { + buffer = malloc(size); + assert(buffer); + } + + // Set buffer pointer and size + fp->Buffer = buffer; + fp->BufferSpace = size; + + // Set mode flag + if( mode == _IOLBF ) + fp->Flags |= FILE_FLAG_LINEBUFFERED; + else + fp->Flags &= ~FILE_FLAG_LINEBUFFERED; + } + + return 0; +} + +int _fflush_int(FILE *fp) +{ + int ret = 0; + size_t len; + + // Check the buffer contains data + if( fp->BufferPos == 0 ) + return 0; + + switch(fp->Flags & FILE_FLAG_MODE_MASK) + { + // Read - Flush input buffer + case FILE_FLAG_MODE_READ: + fp->BufferPos = 0; + break; + + // Append - Seek to end and write + case FILE_FLAG_MODE_APPEND: + _SysSeek(fp->FD, fp->BufferOfs, SEEK_SET); + len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos); + if( len < fp->BufferPos ) + ret = 1; + fp->BufferPos -= len; + fp->BufferOfs = _SysTell(fp->FD); + break; + + // Write - Write buffer + case FILE_FLAG_MODE_WRITE: + _SysDebug("Flushing to %i '%.*s'", fp->FD, fp->BufferPos, fp->Buffer); + len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos); + if( len != fp->BufferPos ) + ret = 1; + fp->BufferPos -= len; + break; + default: + break; + } + return ret; +} + EXPORT void fflush(FILE *fp) { if( !fp || fp->FD == -1 ) return ; - if( !(fp->Flags & FILE_FLAG_DIRTY) ) - return ; - // Nothing to do for memory files if( fp->FD == -2 ) return ; + + _fflush_int(fp); } EXPORT void clearerr(FILE *fp) @@ -238,20 +363,41 @@ EXPORT int fseek(FILE *fp, long int amt, int whence) fp->Pos = amt; break; case SEEK_END: - if( fp->BufferSize < (size_t)amt ) + if( fp->BufferSpace < (size_t)amt ) fp->Pos = 0; else - fp->Pos = fp->BufferSize - amt; + fp->Pos = fp->BufferSpace - amt; break; } - if(fp->Pos > (off_t)fp->BufferSize) { - fp->Pos = fp->BufferSize; + if(fp->Pos > (off_t)fp->BufferSpace) { + fp->Pos = fp->BufferSpace; fp->Flags |= FILE_FLAG_EOF; } return 0; } - else - return _SysSeek(fp->FD, amt, whence); + else { + fflush(fp); + _SysSeek(fp->FD, amt, whence); + fp->Pos = _SysTell(fp->FD); + return 0; + } +} + +size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data) +{ + size_t ret = 0, bytes; + while( num -- ) + { + bytes = _SysWrite(fp->FD, data, size); + if( bytes != size ) { + _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size); + _SysSeek(fp->FD, -bytes, SEEK_CUR); + break; + } + data = (char*)data + size; + } + fp->Pos += ret * size; + return ret; } /** @@ -267,19 +413,57 @@ EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp) if( size == 0 || num == 0 ) return 0; + // Handle memory files first if( fp->FD == -2 ) { - size_t avail = (fp->BufferSize - fp->Pos) / size; + size_t avail = (fp->BufferSpace - fp->Pos) / size; if( avail == 0 ) fp->Flags |= FILE_FLAG_EOF; - if( num > avail ) num = avail; + if( num > avail ) + num = avail; size_t bytes = num * size; - memcpy((char*)fp->Buffer + fp->Pos, ptr, bytes); + memcpy(fp->Buffer + fp->Pos, ptr, bytes); fp->Pos += bytes; - ret = num; + return num; } - else { - ret = _SysWrite(fp->FD, ptr, size*num); - ret /= size; + + switch( _GetFileMode(fp) ) + { + case FILE_FLAG_MODE_READ: + case FILE_FLAG_MODE_EXEC: + return 0; + case FILE_FLAG_MODE_APPEND: + fseek(fp, 0, SEEK_END); + case FILE_FLAG_MODE_WRITE: + if( fp->BufferSpace ) + { + // Buffering enabled + if( fp->BufferSpace - fp->BufferPos < size*num ) + { + // If there's not enough space, flush and write-through + _fflush_int(fp); // TODO: check ret + ret = _fwrite_unbuffered(fp, size, num, ptr); + } + else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) ) + { + // Newline present? Flush though + _fflush_int(fp); // TODO: check ret + ret = _fwrite_unbuffered(fp, size, num, ptr); + } + else + { + // Copy to buffer + memcpy( fp->Buffer + fp->BufferPos, ptr, size*num ); + fp->BufferPos += size*num; + ret = num; + } + } + else + { + // Bufering disabled, write-though + ret = _fwrite_unbuffered(fp, size, num, ptr); + } + // errno should be set earlier + break; } return ret; @@ -299,26 +483,26 @@ EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp) return 0; if( fp->FD == -2 ) { - size_t avail = (fp->BufferSize - fp->Pos) / size; + size_t avail = (fp->BufferSpace - fp->Pos) / size; if( avail == 0 ) fp->Flags |= FILE_FLAG_EOF; if( num > avail ) num = avail; size_t bytes = num * size; - memcpy(ptr, (char*)fp->Buffer + fp->Pos, bytes); + memcpy(ptr, fp->Buffer + fp->Pos, bytes); fp->Pos += bytes; - ret = num; + return num; } - else { - ret = _SysRead(fp->FD, ptr, size*num); - if( ret == (size_t)-1) - return -1; - if( ret == 0 && size*num > 0 ) { - fp->Flags |= FILE_FLAG_EOF; - return 0; - } - ret /= size; + + // Standard file + ret = _SysRead(fp->FD, ptr, size*num); + if( ret == (size_t)-1) + return -1; + if( ret == 0 && size*num > 0 ) { + fp->Flags |= FILE_FLAG_EOF; + return 0; } - + ret /= size; + return ret; } diff --git a/Usermode/Libraries/libc.so_src/stdio_int.h b/Usermode/Libraries/libc.so_src/stdio_int.h index f87da812..ab5b144d 100644 --- a/Usermode/Libraries/libc.so_src/stdio_int.h +++ b/Usermode/Libraries/libc.so_src/stdio_int.h @@ -10,16 +10,21 @@ #include // === CONSTANTS === -#define FILE_FLAG_MODE_MASK 0x0007 #define FILE_FLAG_MODE_READ 0x0001 #define FILE_FLAG_MODE_WRITE 0x0002 #define FILE_FLAG_MODE_EXEC 0x0003 #define FILE_FLAG_MODE_APPEND 0x0004 +#define FILE_FLAG_MODE_MASK 0x0007 + #define FILE_FLAG_M_EXT 0x0010 #define FILE_FLAG_M_BINARY 0x0020 + #define FILE_FLAG_EOF 0x0100 -#define FILE_FLAG_DIRTY 0x0200 -#define FILE_FLAG_ALLOC 0x1000 +#define FILE_FLAG_LINEBUFFERED 0x1000 // Flush when '\n' encountered + +#define FILE_FLAG_ALLOC 0x8000 // Internal 'is used' flag + +#define _GetFileMode(fp) ((fp)->Flags & FILE_FLAG_MODE_MASK) // === TYPES === struct sFILE { @@ -30,9 +35,10 @@ struct sFILE { #if DEBUG_BUILD char *FileName; // heap #endif - void *Buffer; - off_t BufferStart; - size_t BufferSize; + off_t BufferOfs; // File offset of first byte in buffer + char *Buffer; + size_t BufferPos; // First unused byte in the buffer (read/write pos essentially) + size_t BufferSpace; // Number of bytes allocated in \a Buffer }; #endif diff --git a/Usermode/Libraries/libc.so_src/stdlib.c b/Usermode/Libraries/libc.so_src/stdlib.c index 6e81c654..e05ddc2d 100644 --- a/Usermode/Libraries/libc.so_src/stdlib.c +++ b/Usermode/Libraries/libc.so_src/stdlib.c @@ -7,21 +7,34 @@ #include #include "lib.h" -extern void *_crt0_exit_handler; +extern void _stdio_cleanup(void); + +#define MAX_ATEXIT_HANDLERS 64 // Standard defines >=32 // === PROTOTYPES === EXPORT int atoi(const char *str); EXPORT void exit(int status); // === GLOBALS === -void (*g_stdlib_exithandler)(void); +typedef void (*stdlib_exithandler_t)(void); +stdlib_exithandler_t g_stdlib_exithandlers[MAX_ATEXIT_HANDLERS]; + int g_stdlib_num_exithandlers; // === CODE === -void atexit(void (*__func)(void)) +void _call_atexit_handlers(void) +{ + int i; + for( i = g_stdlib_num_exithandlers; i --; ) + g_stdlib_exithandlers[i](); + _stdio_cleanup(); +} + +EXPORT void atexit(void (*__func)(void)) { - g_stdlib_exithandler = __func; - // TODO: Replace with meta-function to allow multiple atexit() handlers - _crt0_exit_handler = __func; + if( g_stdlib_num_exithandlers < MAX_ATEXIT_HANDLERS ) + { + g_stdlib_exithandlers[g_stdlib_num_exithandlers++] = __func; + } } /** @@ -30,8 +43,7 @@ void atexit(void (*__func)(void)) */ EXPORT void exit(int status) { - if( g_stdlib_exithandler ) - g_stdlib_exithandler(); + _call_atexit_handlers(); _exit(status); } diff --git a/Usermode/Libraries/libc.so_src/stub.c b/Usermode/Libraries/libc.so_src/stub.c index 7e535a7d..5f16c05e 100644 --- a/Usermode/Libraries/libc.so_src/stub.c +++ b/Usermode/Libraries/libc.so_src/stub.c @@ -23,14 +23,12 @@ static void cpuid(uint32_t Num, uint32_t *EAX, uint32_t *EBX, uint32_t *EDX, uin // === IMPORTS === extern tLoadedLib gLoadedLibraries[64]; -extern int _SysSetFaultHandler(int (*Handler)(int)); +extern void *_crt0_exit_handler; +extern void _stdio_init(void); +extern void _call_atexit_handlers(void); // === GLOBALS === extern char **_envp; -extern struct sFILE _iob[]; -extern struct sFILE *stdin; -extern struct sFILE *stdout; -extern struct sFILE *stderr; // --- CPU Features --- #if USE_CPUID tCPUID gCPU_Features; @@ -61,14 +59,8 @@ int SoMain(UNUSED(uintptr_t, BaseAddress), UNUSED(int, argc), UNUSED(char **, ar } } #endif - - // Init FileIO Pointers - stdin = &_iob[0]; - stdin->FD = 0; stdin->Flags = FILE_FLAG_MODE_READ; - stdout = &_iob[1]; - stdout->FD = 1; stdout->Flags = FILE_FLAG_MODE_WRITE; - stderr = &_iob[2]; - stderr->FD = 2; stderr->Flags = FILE_FLAG_MODE_WRITE; + + _stdio_init(); #if USE_CPUID { @@ -79,6 +71,8 @@ int SoMain(UNUSED(uintptr_t, BaseAddress), UNUSED(int, argc), UNUSED(char **, ar } #endif + _crt0_exit_handler = _call_atexit_handlers; + // Set Error handler _SysSetFaultHandler(ErrorHandler); -- 2.20.1