void exitSection(int id) { // LEAVE THESE STATEMENTS int isFemale = femaleOnly ? 1 : (id % 2 == 0); // TODO: Complete this function if (isFemale) { mutexLock(id, &femaleCountMutex); //pthread_mutex_lock(&femaleCountMutex); femaleCount--; if (femaleCount == 0) { // sem_post(&male); semPost(id, &male); } mutexUnlock(id, &femaleCountMutex); //pthread_mutex_unlock(&femaleCountMutex); } else { mutexLock(id, &maleCountMutex); //pthread_mutex_lock(&maleCountMutex); maleCount--; if (maleCount == 0) { // sem_post(&female); semPost(id, &female); } mutexUnlock(id, &maleCountMutex); //pthread_mutex_unlock(&maleCountMutex); } }
off_t lseek(int fid, off_t offset, int whence) { off_t rc; FILE *file; #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If the file descriptor is invalid, return error. */ /*-------------------------------------------------------------------*/ if (fid < 0 || fid >= FOPEN_MAX) { set_errno(EBADF); return -1; } if (Files[fid].flags & FCB_DIR) { set_errno(EISDIR); return -1; } #endif /*-------------------------------------------------------------------*/ /* Get exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Return error if file is closed. */ /*-------------------------------------------------------------------*/ file = &Files[fid]; if (file->ioctl == NULL) { set_errno(EBADF); semPost(FileSysSem); return -1; } /*-------------------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*-------------------------------------------------------------------*/ file->acquire(file, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Call file system specific FSEEK routine. */ /*-------------------------------------------------------------------*/ rc = (off_t)file->ioctl(file, FSEEK, (long)offset, whence); /*-------------------------------------------------------------------*/ /* Release exclusive access to file systems and return result. */ /*-------------------------------------------------------------------*/ file->release(file, F_READ | F_WRITE); semPost(FileSysSem); return rc; }
//------------------------------------------------------------------------------ void WorkerThread::process(unsigned int count, const char * const *arguments, Shell::ShellContext *context) { baseContext = context; argumentCount = count; firstArgument = arguments; semPost(&semaphore); }
//------------------------------------------------------------------------------ void ThreadSwarm::onCommandCompleted(WorkerThread *worker, result res) { mutexLock(&queueLock); pool.push(worker); results.push(res); mutexUnlock(&queueLock); semPost(&queueSynchronizer); }
void MyQueue::enqueue(AsyncCallState* state) { state->nextInQueue = NULL; lockMutex(&queueMutex); if (tail == NULL) { head = state; } else { tail->nextInQueue = state; } tail = state; unlockMutex(&queueMutex); semPost(&queueSem); }
int fcntl(int fid, int cmd, ...) { va_list ap; FILE *file; #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If the file descriptor is invalid, return error. */ /*-------------------------------------------------------------------*/ if (fid < 0 || fid >= FOPEN_MAX) { set_errno(EBADF); return -1; } #endif /*-------------------------------------------------------------------*/ /* Get exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Return error if file is closed. */ /*-------------------------------------------------------------------*/ file = &Files[fid]; if (file->ioctl == NULL) { semPost(FileSysSem); set_errno(EBADF); return -1; } /*-------------------------------------------------------------------*/ /* Based on the command, execute the right instructions. */ /*-------------------------------------------------------------------*/ switch (cmd) { case F_DUPFD: { /*---------------------------------------------------------------*/ /* Use the va_arg mechanism to get the argument. */ /*---------------------------------------------------------------*/ va_start(ap, cmd); fid = va_arg(ap, int); va_end(ap); #if OS_PARM_CHECK /*---------------------------------------------------------------*/ /* If the file descriptor is invalid, return error. */ /*---------------------------------------------------------------*/ if (fid < 0 || fid >= FOPEN_MAX) { semPost(FileSysSem); set_errno(EINVAL); return -1; } #endif /*---------------------------------------------------------------*/ /* Look for free file control block identifier >= fid. */ /*---------------------------------------------------------------*/ for (;;) { FILE *file2 = &Files[fid]; /*-------------------------------------------------------------*/ /* Check if file control block is free. */ /*-------------------------------------------------------------*/ if (file2->ioctl == NULL) { /*-----------------------------------------------------------*/ /* Copy previous file control block to new one. */ /*-----------------------------------------------------------*/ *file2 = *file; /*-----------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*-----------------------------------------------------------*/ file2->acquire(file2, F_READ | F_WRITE); /*-----------------------------------------------------------*/ /* Call file system specific DUP routine. */ /*-----------------------------------------------------------*/ file2->ioctl(file2, DUP); /*-----------------------------------------------------------*/ /* Release exclusive access to lower file system. */ /*-----------------------------------------------------------*/ file2->release(file2, F_READ | F_WRITE); break; } /*-------------------------------------------------------------*/ /* If none are free, set errno and break. */ /*-------------------------------------------------------------*/ if (++fid >= FOPEN_MAX) { set_errno(EMFILE); fid = -1; break; } } /*---------------------------------------------------------------*/ /* Release access to upper file system and return result. */ /*---------------------------------------------------------------*/ semPost(FileSysSem); return fid; } case F_SETFL: { int oflag, r_val; /*---------------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*---------------------------------------------------------------*/ file->acquire(file, F_READ | F_WRITE); /*---------------------------------------------------------------*/ /* Use the va_arg mechanism to get the argument. */ /*---------------------------------------------------------------*/ va_start(ap, cmd); oflag = va_arg(ap, int); va_end(ap); /*---------------------------------------------------------------*/ /* Call specific ioctl to set flag. */ /*---------------------------------------------------------------*/ r_val = (int)file->ioctl(file, SET_FL, oflag); /*---------------------------------------------------------------*/ /* Release exclusive access to lower file system. */ /*---------------------------------------------------------------*/ file->release(file, F_READ | F_WRITE); /*---------------------------------------------------------------*/ /* Release access to upper file system and return result. */ /*---------------------------------------------------------------*/ semPost(FileSysSem); return r_val; } case F_GETFL: { int r_val; /*---------------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*---------------------------------------------------------------*/ file->acquire(file, F_READ); /*---------------------------------------------------------------*/ /* Call specific ioctl to get flag. */ /*---------------------------------------------------------------*/ r_val = (int)file->ioctl(file, GET_FL); /*---------------------------------------------------------------*/ /* Release exclusive access to lower file system. */ /*---------------------------------------------------------------*/ file->release(file, F_READ); /*---------------------------------------------------------------*/ /* Release access to upper file system and return result. */ /*---------------------------------------------------------------*/ semPost(FileSysSem); return r_val; } } /*-------------------------------------------------------------------*/ /* An unsupported command was requested. */ /*-------------------------------------------------------------------*/ set_errno(EINVAL); semPost(FileSysSem); return -1; }
//------------------------------------------------------------------------------ result ThreadSwarm::run(unsigned int count, const char * const *arguments, Shell::ShellContext *context) { bool help = false; for (unsigned int i = 0; i < count; ++i) { if (!strcmp(arguments[i], "--help")) { help = true; continue; } } if (help) { owner.log("Usage: swarm COMMAND ! COMMAND..."); owner.log(" --help print help message"); return E_OK; } unsigned int index = 0; result res = E_OK; while (index < count) { const unsigned int first = index; while (index < count && strcmp(arguments[index], "!")) ++index; if (index > first) { semWait(&queueSynchronizer); mutexLock(&queueLock); while (!results.empty()) { res = results.front(); results.pop(); if (res != E_OK) break; } if (res != E_OK) { semPost(&queueSynchronizer); break; } WorkerThread * const thread = pool.front(); pool.pop(); mutexUnlock(&queueLock); thread->process(index - first, arguments + first, context); } ++index; } //Wait for commands to complete for (index = 0; index < THREAD_COUNT; ++index) semWait(&queueSynchronizer); //Release acquired resources for (index = 0; index < THREAD_COUNT; ++index) semPost(&queueSynchronizer); //Clear result queue while (!results.empty()) { if (res == E_OK) res = results.front(); results.pop(); } return res; }
//------------------------------------------------------------------------------ void WorkerThread::terminate() { finalize = true; semPost(&semaphore); }
int enable_sync(const char *path) { FileSys *fsys; int rv = -1; #if !_PATH_NO_TRUNC char trunc_path[PATH_MAX + 1]; #endif #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* Check that both path and buf are valid parameters. */ /*-------------------------------------------------------------------*/ if (path == NULL) { set_errno(EFAULT); return -1; } #endif /*-------------------------------------------------------------------*/ /* If path too long, return error if no truncation, else truncate. */ /*-------------------------------------------------------------------*/ if (strlen(path) > PATH_MAX) { #if _PATH_NO_TRUNC set_errno(ENAMETOOLONG); return -1; #else strncpy(trunc_path, path, PATH_MAX); trunc_path[PATH_MAX] = '\0'; path = trunc_path; #endif } /*-------------------------------------------------------------------*/ /* Acquire exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Check that 'path' is the name of a mounted volume. */ /*-------------------------------------------------------------------*/ for (fsys = MountedList.head;; fsys = fsys->next) { /*-----------------------------------------------------------------*/ /* If the volume is not mounted, return error. */ /*-----------------------------------------------------------------*/ if (fsys == NULL) { set_errno(ENOENT); goto end; } /*-----------------------------------------------------------------*/ /* If the volume was found, stop looking. */ /*-----------------------------------------------------------------*/ if (!strcmp(fsys->name, path)) break; } /*-------------------------------------------------------------------*/ /* Call file system specific ENABLE_SYNC routine. */ /*-------------------------------------------------------------------*/ rv = (int)fsys->ioctl(NULL, ENABLE_SYNC, fsys->volume); /*-------------------------------------------------------------------*/ /* Release exclusive access to upper file system and return. */ /*-------------------------------------------------------------------*/ end: semPost(FileSysSem); return rv; }
int mount(const char *path) { Module fp; FileSys *fsys; int i, rv = -1; char *name; #if !_PATH_NO_TRUNC char trunc_path[PATH_MAX + 1]; #endif #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If path is NULL, return -1. */ /*-------------------------------------------------------------------*/ if (path == NULL) { set_errno(EFAULT); return -1; } #endif /*-------------------------------------------------------------------*/ /* Strip leading '\' or '/'. */ /*-------------------------------------------------------------------*/ if (*path == '/' || *path == '\\') ++path; /*-------------------------------------------------------------------*/ /* If path too long, return error if no truncation, else truncate. */ /*-------------------------------------------------------------------*/ if (strlen(path) > PATH_MAX) { #if _PATH_NO_TRUNC set_errno(ENAMETOOLONG); return -1; #else strncpy(trunc_path, path, PATH_MAX); trunc_path[PATH_MAX] = '\0'; path = trunc_path; #endif } /*-------------------------------------------------------------------*/ /* Get exclusive access to file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* If there is a name duplication, return error. */ /*-------------------------------------------------------------------*/ for (fsys = MountedList.head; fsys; fsys = fsys->next) if (!strcmp(fsys->name, path)) { set_errno(EEXIST); goto end; } /*-------------------------------------------------------------------*/ /* Go through the module list and have the corresponding module */ /* perform the actual mount. The module should be able to recognize */ /* the name of the device to be mounted. */ /*-------------------------------------------------------------------*/ for (i = 0;; ++i) { fp = ModuleList[i]; /*-----------------------------------------------------------------*/ /* If device is not found, return error. */ /*-----------------------------------------------------------------*/ if (!fp) { set_errno(ENOENT); goto end; } /*-----------------------------------------------------------------*/ /* If module finds the device, stop with error if mount fails, */ /* else stop successfully. */ /*-----------------------------------------------------------------*/ fsys = fp(kMount, path, &name, FALSE); if (fsys == (FileSys *)-1) goto end; else if (fsys) break; } /*-------------------------------------------------------------------*/ /* Add device to list of mounted devices. */ /*-------------------------------------------------------------------*/ if (MountedList.head) { fsys->prev = MountedList.tail; MountedList.tail->next = fsys; } else { fsys->prev = NULL; MountedList.head = fsys; } fsys->next = NULL; MountedList.tail = fsys; rv = 0; /*-------------------------------------------------------------------*/ /* Release exclusive access to upper file system and return. */ /*-------------------------------------------------------------------*/ end: semPost(FileSysSem); return rv; }
DIR *opendir(const char *dirname) { DIR *dir; void *ptr; #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If path is NULL, return NULL. */ /*-------------------------------------------------------------------*/ if (dirname == NULL) { set_errno(ENOTDIR); return NULL; } #endif /*-------------------------------------------------------------------*/ /* Acquire exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Find a free file control block and initialize it. */ /*-------------------------------------------------------------------*/ for (dir = &Files[0]; dir->ioctl; ++dir) { /*-----------------------------------------------------------------*/ /* If no DIR was found, return error. */ /*-----------------------------------------------------------------*/ if (dir == &Files[DIROPEN_MAX - 1]) { set_errno(ENOMEM); semPost(FileSysSem); return NULL; } } FsInitFCB(dir, FCB_DIR); /*-------------------------------------------------------------------*/ /* Ensure that path is valid and assign ioctl callback function. */ /*-------------------------------------------------------------------*/ ptr = FSearch(dir, &dirname, ACTUAL_DIR); if (ptr == NULL) { dir->ioctl = NULL; semPost(FileSysSem); return NULL; } /*-------------------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*-------------------------------------------------------------------*/ dir->acquire(dir, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Call file system specific OPENDIR routine. */ /*-------------------------------------------------------------------*/ ptr = dir->ioctl(dir, OPENDIR, ptr); /*-------------------------------------------------------------------*/ /* Release exclusive access to lower file system. */ /*-------------------------------------------------------------------*/ dir->release(dir, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Release file control block if an error occurred. */ /*-------------------------------------------------------------------*/ if (ptr == NULL) dir->ioctl = NULL; /*-------------------------------------------------------------------*/ /* Release upper file system access and return result. */ /*-------------------------------------------------------------------*/ semPost(FileSysSem); return ptr; }
int creatn(const char *path, mode_t mode, size_t size) { int rv = -1; void *dir; FILE *file; #if !_PATH_NO_TRUNC char trunc_path[PATH_MAX + 1]; #endif #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If path is NULL, return -1. */ /*-------------------------------------------------------------------*/ if (path == NULL) { set_errno(EFAULT); return -1; } #endif /*-------------------------------------------------------------------*/ /* Acquire exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Find a free file control block and initialize it. */ /*-------------------------------------------------------------------*/ for (file = &Files[0]; file->ioctl; ++file) { /*-----------------------------------------------------------------*/ /* If none are free, return error. */ /*-----------------------------------------------------------------*/ if (file == &Files[FOPEN_MAX - 1]) { set_errno(EMFILE); semPost(FileSysSem); return -1; } } FsInitFCB(file, FCB_FILE); /*-------------------------------------------------------------------*/ /* Ensure path is valid. */ /*-------------------------------------------------------------------*/ dir = FSearch(file, &path, PARENT_DIR); if (dir == NULL) goto end; /*-------------------------------------------------------------------*/ /* If path too long, return error if no truncation, else truncate. */ /*-------------------------------------------------------------------*/ if (strlen(path) > PATH_MAX) { #if _PATH_NO_TRUNC set_errno(ENAMETOOLONG); goto end; #else strncpy(trunc_path, path, PATH_MAX); trunc_path[PATH_MAX] = '\0'; path = trunc_path; #endif } /*-------------------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*-------------------------------------------------------------------*/ file->acquire(file, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Call file system specific CREATN routine. */ /*-------------------------------------------------------------------*/ rv = (int)file->ioctl(file, CREATN, path, mode, size, dir); /*-------------------------------------------------------------------*/ /* Release exclusive access to lower file system. */ /*-------------------------------------------------------------------*/ file->release(file, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Free control block if error, else set return value. */ /*-------------------------------------------------------------------*/ end: if (rv == -1) file->ioctl = NULL; else rv = file - &Files[0]; /*-------------------------------------------------------------------*/ /* Release exclusive access to upper file system and return result. */ /*-------------------------------------------------------------------*/ semPost(FileSysSem); return rv; }
FILE *freopen(const char *filename, const char *mode, FILE *file) { void *dir; FILE *rv = NULL; #if !_PATH_NO_TRUNC char trunc_filename[PATH_MAX + 1]; #endif #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If file handle is invalid, return NULL. */ /*-------------------------------------------------------------------*/ if (InvalidStream(file)) { set_errno(EBADF); return NULL; } /*-------------------------------------------------------------------*/ /* If file name is NULL, return NULL. */ /*-------------------------------------------------------------------*/ if (filename == NULL) { file->errcode = EINVAL; set_errno(EINVAL); return NULL; } #endif /*-------------------------------------------------------------------*/ /* Get exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Flush and close the file, acquiring and releasing volume access. */ /*-------------------------------------------------------------------*/ file->acquire(file, F_READ | F_WRITE); file->ioctl(file, FFLUSH); file->ioctl(file, FCLOSE); file->release(file, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Verify path of new file. Error exit if path is invalid. */ /*-------------------------------------------------------------------*/ dir = FSearch(file, &filename, PARENT_DIR); if (dir == NULL) goto end; /*-------------------------------------------------------------------*/ /* If path too long, return error if no truncation, else truncate. */ /*-------------------------------------------------------------------*/ if (strlen(filename) > PATH_MAX) { #if _PATH_NO_TRUNC set_errno(ENAMETOOLONG); goto end; #else strncpy(trunc_filename, filename, PATH_MAX); trunc_filename[PATH_MAX] = '\0'; filename = trunc_filename; #endif } /*-------------------------------------------------------------------*/ /* Open the file, acquiring and releasing volume access. */ /*-------------------------------------------------------------------*/ file->acquire(file, F_READ | F_WRITE); rv = file->ioctl(file, FOPEN, filename, mode, dir); file->release(file, F_READ | F_WRITE); end: /*-------------------------------------------------------------------*/ /* Free control block if error. Release access to upper file system. */ /*-------------------------------------------------------------------*/ if (rv == NULL) file->ioctl = NULL; semPost(FileSysSem); return rv; }
int mkdir(const char *path, mode_t mode) { void *parent; int r_value = -1; DIR dir; #if !_PATH_NO_TRUNC char trunc_path[PATH_MAX + 1]; #endif #if OS_PARM_CHECK /*-------------------------------------------------------------------*/ /* If path is NULL, return -1. */ /*-------------------------------------------------------------------*/ if (path == NULL) { set_errno(EFAULT); return -1; } #endif /*-------------------------------------------------------------------*/ /* Initialize temporary file control block. */ /*-------------------------------------------------------------------*/ FsInitFCB(&dir, FCB_DIR); /*-------------------------------------------------------------------*/ /* Get exclusive access to the file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* Ensure that path is valid. */ /*-------------------------------------------------------------------*/ parent = FSearch(&dir, &path, PARENT_DIR); if (parent == NULL) goto end; /*-------------------------------------------------------------------*/ /* If path too long, return error if no truncation, else truncate. */ /*-------------------------------------------------------------------*/ if (strlen(path) > PATH_MAX) { #if _PATH_NO_TRUNC set_errno(ENAMETOOLONG); goto end; #else strncpy(trunc_path, path, PATH_MAX); trunc_path[PATH_MAX] = '\0'; path = trunc_path; #endif } /*-------------------------------------------------------------------*/ /* Acquire exclusive access to lower file system. */ /*-------------------------------------------------------------------*/ dir.acquire(&dir, F_READ | F_WRITE); /*-------------------------------------------------------------------*/ /* Call file system specific MKDIR routine. */ /*-------------------------------------------------------------------*/ r_value = (int)dir.ioctl(&dir, MKDIR, path, mode, parent, 0, 0); /*-------------------------------------------------------------------*/ /* Release exclusive access to file systems and return result. */ /*-------------------------------------------------------------------*/ dir.release(&dir, F_READ | F_WRITE); end: semPost(FileSysSem); return r_value; }
int fflush(FILE *file) { int rv = 0; /*-------------------------------------------------------------------*/ /* Acquire exclusive access to upper file system. */ /*-------------------------------------------------------------------*/ semPend(FileSysSem, WAIT_FOREVER); /*-------------------------------------------------------------------*/ /* If file is not NULL, flush just the specified file. */ /*-------------------------------------------------------------------*/ if (file) { #if OS_PARM_CHECK /*-----------------------------------------------------------------*/ /* Return error if file handle is invalid. */ /*-----------------------------------------------------------------*/ if (InvalidStream(file)) { set_errno(EBADF); semPost(FileSysSem); return EOF; } #endif /*-----------------------------------------------------------------*/ /* Return error if file is closed. */ /*-----------------------------------------------------------------*/ if (file->ioctl == NULL) { set_errno(EBADF); semPost(FileSysSem); return EOF; } /*-----------------------------------------------------------------*/ /* Acquire exclusive access to stream. */ /*-----------------------------------------------------------------*/ file->acquire(file, F_WRITE); /*-----------------------------------------------------------------*/ /* Call file system specific FFLUSH routine. */ /*-----------------------------------------------------------------*/ rv = (int)file->ioctl(file, FFLUSH); /*-----------------------------------------------------------------*/ /* Release exclusive access to stream. */ /*-----------------------------------------------------------------*/ file->release(file, F_WRITE); } /*-------------------------------------------------------------------*/ /* Else flush all open files. */ /*-------------------------------------------------------------------*/ else { /*-----------------------------------------------------------------*/ /* Loop over every file. */ /*-----------------------------------------------------------------*/ for (file = &Files[0]; file < &Files[FOPEN_MAX]; ++file) { /*---------------------------------------------------------------*/ /* Check if file is open. */ /*---------------------------------------------------------------*/ if (file->ioctl) { /*-------------------------------------------------------------*/ /* Acquire exclusive write access to lower file system. */ /*-------------------------------------------------------------*/ file->acquire(file, F_WRITE); /*-------------------------------------------------------------*/ /* If any flush fails, set return value to -1. */ /*-------------------------------------------------------------*/ if (file->ioctl(file, FFLUSH)) rv = EOF; /*-------------------------------------------------------------*/ /* Release exclusive write access to lower file system. */ /*-------------------------------------------------------------*/ file->release(file, F_WRITE); } } } /*-------------------------------------------------------------------*/ /* Release exclusive access to upper file system and return result. */ /*-------------------------------------------------------------------*/ semPost(FileSysSem); return rv; }