fileCache_t *addFileCache( int iFd, char *objPath, char *localPath, char *cachePath, int mode, rodsLong_t fileSize, cacheState_t state ) { uint cachedTime = time( 0 ); fileCache_t *fileCache, *swapCache; if ( state == NO_FILE_CACHE ) { return newFileCache( iFd, objPath, localPath, cachePath, cachedTime, mode, fileSize, state ); } else { fileCache = newFileCache( iFd, objPath, localPath, cachePath, cachedTime, mode, fileSize, state ); LOCK_STRUCT( *FileCacheList ); if ( _listSize( FileCacheList ) >= NUM_NEWLY_CREATED_SLOT ) { ListNode *node = FileCacheList->list->head; swapCache = ( fileCache_t * ) node->value; listRemoveNoRegion( FileCacheList->list, node ); UNLOCK_STRUCT( *FileCacheList ); ifuseFileCacheSwapOut( swapCache ); LOCK_STRUCT( *FileCacheList ); listAppendNoRegion( FileCacheList->list, fileCache ); } UNLOCK_STRUCT( *FileCacheList ); return fileCache; } }
int renameLocalPath( PathCacheTable *pctable, char *from, char *to, char *toIrodsPath ) { /* do not check existing path here as path cache may be out of date */ pathCache_t *fromPathCache = matchPathCache( pctable, from ); if ( NULL == fromPathCache ) { return 0; } LOCK_STRUCT( *fromPathCache ); if ( fromPathCache->fileCache != NULL ) { LOCK_STRUCT( *( fromPathCache->fileCache ) ); free( fromPathCache->fileCache->localPath ); fromPathCache->fileCache->localPath = strdup( to ); free( fromPathCache->fileCache->objPath ); fromPathCache->fileCache->objPath = strdup( toIrodsPath ); UNLOCK_STRUCT( *( fromPathCache->fileCache ) ); } pathCache_t *tmpPathCache = NULL; LOCK( *pctable -> PathCacheLock ); pathReplace( pctable, ( char * ) to, fromPathCache->fileCache, &fromPathCache->stbuf, &tmpPathCache ); pathNotExist( pctable, ( char * ) from ); UNLOCK( *pctable -> PathCacheLock ); UNLOCK_STRUCT( *fromPathCache ); return 0; }
void connManager() { time_t curTime; iFuseConn_t *tmpIFuseConn; ListNode *node; List *TimeOutList = newListNoRegion(); while ( 1 ) { curTime = time( NULL ); /* exceed high water mark for number of connection ? */ if ( listSize( ConnectedConn ) > HIGH_NUM_CONN ) { LOCK_STRUCT( *ConnectedConn ); int disconnTarget = _listSize( ConnectedConn ) - HIGH_NUM_CONN; node = ConnectedConn->list->head; while ( node != NULL ) { tmpIFuseConn = ( iFuseConn_t * ) node->value; if ( tmpIFuseConn->inuseCnt == 0 && tmpIFuseConn->pendingCnt == 0 && curTime - tmpIFuseConn->actTime > IFUSE_CONN_TIMEOUT ) { listAppendNoRegion( TimeOutList, tmpIFuseConn ); } node = node->next; } UNLOCK_STRUCT( *ConnectedConn ); node = TimeOutList->head; int disconned = 0; while ( node != NULL && disconned < disconnTarget ) { tmpIFuseConn = ( iFuseConn_t * ) node->value; LOCK_STRUCT( *tmpIFuseConn ); if ( tmpIFuseConn->inuseCnt == 0 && tmpIFuseConn->pendingCnt == 0 && curTime - tmpIFuseConn->actTime > IFUSE_CONN_TIMEOUT ) { removeFromConcurrentList2( FreeConn, tmpIFuseConn ); removeFromConcurrentList2( ConnectedConn, tmpIFuseConn ); if ( tmpIFuseConn->status == 0 ) { /* no struct is referring to it, we can unlock it and free it */ UNLOCK_STRUCT( *tmpIFuseConn ); /* rodsLog(LOG_ERROR, "[FREE IFUSE CONN] %s:%d %p", __FILE__, __LINE__, tmpIFuseConn); */ _freeIFuseConn( tmpIFuseConn ); } else { /* set to timed out */ _ifuseDisconnect( tmpIFuseConn ); UNLOCK_STRUCT( *tmpIFuseConn ); } disconned ++; } else { UNLOCK_STRUCT( *tmpIFuseConn ); } node = node->next; } clearListNoRegion( TimeOutList ); } notifyWait( &connReqWait.mutex, &connReqWait.cond ); timeoutWait( &ConnManagerLock, &ConnManagerCond, CONN_MANAGER_SLEEP_TIME ); } deleteListNoRegion( TimeOutList ); }
/* close a iFuse file */ int ifuseClose (iFuseDesc_t *desc) { int status; LOCK_STRUCT(*desc); status = _ifuseFlush(desc); if(status < 0) { rodsLogError(LOG_ERROR, status, "ifuseClose: flush of %s error", desc->localPath); } status = ifuseFileCacheClose(desc->fileCache); if(status < 0) { rodsLogError(LOG_ERROR, status, "ifuseClose: close of %s error", desc->localPath); } status = _freeIFuseDesc(desc); UNLOCK_STRUCT(*desc); if(status < 0) { rodsLogError(LOG_ERROR, status, "ifuseClose: free desc struct error", desc->localPath); } return status; }
/* precond: fileCache locked or single thread use */ iFuseDesc_t *newIFuseDesc (char *objPath, char *localPath, fileCache_t *fileCache, int *status) { iFuseDesc_t *desc; LOCK_STRUCT(*IFuseDescFreeList); /* printf("******************************************************\n"); printf("num of desc slots free = %d\n", _listSize(IFuseDescFreeList)); printf("******************************************************\n"); */ if(_listSize(IFuseDescFreeList)!=0) { ListNode *node = IFuseDescFreeList->list->head; desc = (iFuseDesc_t *) node->value; listRemoveNoRegion(IFuseDescFreeList->list, node); UNLOCK_STRUCT(*IFuseDescFreeList); REF_NO_LOCK(desc->fileCache, fileCache); desc->objPath = strdup (objPath); desc->localPath = strdup (localPath); INIT_STRUCT_LOCK(*desc); *status = 0; return desc; } else { UNLOCK_STRUCT(*IFuseDescFreeList); rodsLog (LOG_ERROR, "allocIFuseDesc: Out of iFuseDesc"); *status = SYS_OUT_OF_FILE_DESC; return NULL; } }
int ifuseConnect( iFuseConn_t *iFuseConn, rodsEnv *myRodsEnv ) { int status = 0; LOCK_STRUCT( *iFuseConn ); if ( iFuseConn->conn == NULL ) { rErrMsg_t errMsg; iFuseConn->conn = rcConnect( myRodsEnv->rodsHost, myRodsEnv->rodsPort, myRodsEnv->rodsUserName, myRodsEnv->rodsZone, NO_RECONN, &errMsg ); if ( iFuseConn->conn == NULL ) { /* try one more */ iFuseConn->conn = rcConnect( myRodsEnv->rodsHost, myRodsEnv->rodsPort, myRodsEnv->rodsUserName, myRodsEnv->rodsZone, NO_RECONN, &errMsg ); if ( iFuseConn->conn == NULL ) { rodsLogError( LOG_ERROR, errMsg.status, "ifuseConnect: rcConnect failure %s", errMsg.msg ); UNLOCK_STRUCT( *iFuseConn ); if ( errMsg.status < 0 ) { return errMsg.status; } else { return -1; } } } status = clientLogin( iFuseConn->conn ); if ( status != 0 ) { rcDisconnect( iFuseConn->conn ); iFuseConn->conn = NULL; } } UNLOCK_STRUCT( *iFuseConn ); return status; }
int renmeLocalPath( char *from, char *to, char *toIrodsPath ) { pathCache_t *fromPathCache = NULL; pathCache_t *tmpPathCache = NULL; /* do not check existing path here as path cache may be out of date */ matchAndLockPathCache( from, &fromPathCache ); if ( fromPathCache == NULL ) { return 0; } LOCK( PathCacheLock ); if ( fromPathCache->fileCache != NULL ) { LOCK_STRUCT( *( fromPathCache->fileCache ) ); free( fromPathCache->fileCache->localPath ); fromPathCache->fileCache->localPath = strdup( to ); free( fromPathCache->fileCache->objPath ); fromPathCache->fileCache->objPath = strdup( toIrodsPath ); _pathReplace( ( char * ) to, fromPathCache->fileCache, &fromPathCache->stbuf, &tmpPathCache ); UNLOCK_STRUCT( *( fromPathCache->fileCache ) ); } else { _pathReplace( ( char * ) to, NULL /* fromPathCache->fileCache */, &fromPathCache->stbuf, &tmpPathCache ); } /* need to unlock fileCache here since the following function call ness to lock fileCache */ _pathNotExist( ( char * ) from ); UNLOCK_STRUCT( *fromPathCache ); UNLOCK( PathCacheLock ); return 0; }
void fl_deleteConcurrentList(fl_concurrentList_t *l) { LOCK_STRUCT(*l); fl_clearList(l->list); fl_deleteList(l->list); UNLOCK_STRUCT(*l); free(l); }
int iFuseFileCacheFlush( fileCache_t *fileCache ) { int status; LOCK_STRUCT( *fileCache ); status = _iFuseFileCacheFlush( fileCache ); UNLOCK_STRUCT( *fileCache ); return status; }
int irodsTruncate (const char *path, off_t size) { dataObjInp_t dataObjInp; int status; pathCache_t *tmpPathCache; iFuseConn_t *iFuseConn = NULL; rodsLog (LOG_DEBUG, "irodsTruncate: %s", path); if (matchAndLockPathCache ((char *) path, &tmpPathCache) == 1) { if(tmpPathCache->fileCache != NULL) { LOCK_STRUCT(*tmpPathCache->fileCache); if(tmpPathCache->fileCache->state == HAVE_NEWLY_CREATED_CACHE) { status = truncate (tmpPathCache->fileCache->fileCachePath, size); if (status >= 0) { updatePathCacheStatFromFileCache (tmpPathCache); UNLOCK_STRUCT(*(tmpPathCache->fileCache)); UNLOCK_STRUCT(*tmpPathCache); return (0); } } UNLOCK_STRUCT(*(tmpPathCache->fileCache)); } } UNLOCK_STRUCT(*tmpPathCache); memset (&dataObjInp, 0, sizeof (dataObjInp)); status = parseRodsPathStr ((char *) (path + 1) , &MyRodsEnv, dataObjInp.objPath); if (status < 0) { rodsLogError (LOG_ERROR, status, "irodsTruncate: parseRodsPathStr of %s error", path); /* use ENOTDIR for this type of error */ return -ENOTDIR; } dataObjInp.dataSize = size; getAndUseIFuseConn (&iFuseConn, &MyRodsEnv); RECONNECT_IF_NECESSARY(status, iFuseConn, rcDataObjTruncate (iFuseConn->conn, &dataObjInp)); if (status >= 0) { pathCache_t *tmpPathCache; if (matchAndLockPathCache ((char *) path, &tmpPathCache) == 1) { tmpPathCache->stbuf.st_size = size; } UNLOCK_STRUCT(*tmpPathCache); status = 0; } else { rodsLogError (LOG_ERROR, status, "irodsTruncate: rcDataObjTruncate of %s error", path); status = -ENOENT; } unuseIFuseConn (iFuseConn); return (status); }
int ifuseFlush (int descInx) { int status; LOCK_STRUCT(IFuseDesc[descInx]); status = _ifuseFlush (&IFuseDesc[descInx]); UNLOCK_STRUCT(IFuseDesc[descInx]); return status; }
int useIFuseConn( iFuseConn_t *iFuseConn ) { int status; if ( iFuseConn == NULL || iFuseConn->conn == NULL ) { return USER__NULL_INPUT_ERR; } LOCK_STRUCT( *iFuseConn ); status = _useIFuseConn( iFuseConn ); UNLOCK_STRUCT( *iFuseConn ); return status; }
void* removeFirstElementOfConcurrentList(concurrentList_t *l) { LOCK_STRUCT(*l); void* tmp; if(l->list->head == NULL) { tmp = NULL; } else { tmp = l->list->head->value; listRemoveNoRegion(l->list, l->list->head); } UNLOCK_STRUCT(*l); return tmp; }
/************************************************************************** * private functions **************************************************************************/ static void *_preloadThread(void *arg) { int status; preloadThreadData_t *threadData = (preloadThreadData_t *)arg; preloadThreadInfo_t *threadInfo = NULL; if(threadData == NULL) { rodsLog (LOG_DEBUG, "_preloadThread: given thread argument is null"); pthread_exit(NULL); } threadInfo = threadData->threadInfo; rodsLog (LOG_DEBUG, "_preloadThread: preload - %s", threadData->path); status = _download(threadData->path, &threadData->stbuf); if(status != 0) { rodsLog (LOG_DEBUG, "_preloadThread: download error - %d", status); } // downloading is done LOCK(PreloadLock); // change thread status LOCK_STRUCT(*threadInfo); threadInfo->running = PRELOAD_THREAD_IDLE; UNLOCK_STRUCT(*threadInfo); // release threadData rodsLog (LOG_DEBUG, "_preloadThread: thread finished - %s", threadData->path); if(threadData->path != NULL) { free(threadData->path); threadData->path = NULL; } free(threadData); // remove from hash table removeFromConcurrentList2(PreloadThreadList, threadInfo); deleteFromHashTable(PreloadThreadTable, threadInfo->path); if(threadInfo != NULL) { if(threadInfo->path != NULL) { free(threadInfo->path); threadInfo->path = NULL; } free(threadInfo); } UNLOCK(PreloadLock); pthread_exit(NULL); }
void *fl_removeLastElementOfConcurrentList(fl_concurrentList_t *l) { LOCK_STRUCT(*l); void* tmp; if(l->list->head == NULL) { tmp = NULL; } else { tmp = l->list->tail->value; fl_listRemove(l->list, l->list->tail); } UNLOCK_STRUCT(*l); return tmp; }
int unuseIFuseConn( iFuseConn_t *iFuseConn ) { if ( iFuseConn == NULL || iFuseConn->conn == NULL ) { return USER__NULL_INPUT_ERR; } LOCK_STRUCT( *iFuseConn ); iFuseConn->actTime = time( NULL ); iFuseConn->inuseCnt--; if ( iFuseConn->pendingCnt == 0 ) { addToConcurrentList( FreeConn, iFuseConn ); } UNLOCK_STRUCT( *iFuseConn ); signalConnManager(); return 0; }
/* precond: lock paca */ int _getAndUseConnForPathCache( iFuseConn_t **iFuseConn, pathCache_t *paca ) { int status; /* if connection already closed by connection manager, get new ifuseconn */ if ( paca->iFuseConn != NULL ) { LOCK_STRUCT( *( paca->iFuseConn ) ); if ( paca->iFuseConn->conn != NULL ) { if ( paca->iFuseConn->inuseCnt == 0 ) { _useIFuseConn( paca->iFuseConn ); UNLOCK_STRUCT( *( paca->iFuseConn ) ); /* ifuseReconnect(paca->iFuseConn); */ *iFuseConn = paca->iFuseConn; return 0; } else { UNLOCK_STRUCT( *( paca->iFuseConn ) ); } } else { UNLOCK_STRUCT( *( paca->iFuseConn ) ); /* disconnect by not freed yet */ UNREF( paca->iFuseConn, IFuseConn ); } } iFuseConn_t *tmpIFuseConn; // UNLOCK_STRUCT( *paca ); status = getAndUseIFuseConn( &tmpIFuseConn ); // LOCK_STRUCT( *paca ); if ( status < 0 ) { rodsLog( LOG_ERROR, "ifuseClose: cannot get ifuse connection for %s error, status = %d", paca->localPath, status ); return status; } // if ( paca->iFuseConn != NULL ) { /* has been changed by other threads, or current paca->ifuseconn inuse, * return new ifuseconn without setting paca->ifuseconn */ // *iFuseConn = tmpIFuseConn; //} //else { /* conn in use, cannot be deleted by conn manager * therefore, it is safe to do the following without locking conn */ REF( paca->iFuseConn, tmpIFuseConn ); *iFuseConn = paca->iFuseConn; //} return 0; }
/* getIFuseConnByPath - try to use the same conn as opened desc of the * same path */ iFuseConn_t *getAndUseConnByPath( char *localPath, int *status ) { iFuseConn_t *iFuseConn; /* make sure iFuseConn is not released after getAndLockIFuseDescByPath finishes */ pathCache_t *tmpPathCache = matchPathCache( pctable, localPath ); if ( tmpPathCache ) { LOCK_STRUCT( *tmpPathCache ); *status = _getAndUseConnForPathCache( &iFuseConn, tmpPathCache ); UNLOCK_STRUCT( *tmpPathCache ); } else { /* no match. just assign one */ pathExist( pctable, localPath, NULL, NULL, NULL ); // need to find a way to clean up *status = getAndUseIFuseConn( &iFuseConn ); } return iFuseConn; }
int _useIFuseConn( iFuseConn_t *iFuseConn ) { if ( iFuseConn == NULL || iFuseConn->conn == NULL ) { return USER__NULL_INPUT_ERR; } iFuseConn->actTime = time( NULL ); iFuseConn->pendingCnt++; UNLOCK_STRUCT( *iFuseConn ); /* wait for iFuseConn to be unlocked */ LOCK( iFuseConn->inuseLock ); LOCK_STRUCT( *iFuseConn ); iFuseConn->inuseCnt++; iFuseConn->pendingCnt--; /* move unlock to caller site */ /* UNLOCK (ConnLock); */ return 0; }
/* close any open files inside the file cache */ int ifuseFileCacheClose( fileCache_t *fileCache ) { int status = 0; LOCK_STRUCT( *fileCache ); if ( fileCache->state == NO_FILE_CACHE ) { /* close remote file */ //UNLOCK_STRUCT( *fileCache ); iFuseConn_t *conn = getAndUseConnByPath( fileCache->localPath, &status ); //LOCK_STRUCT( *fileCache ); status = closeIrodsFd( conn->conn, fileCache->iFd ); unuseIFuseConn( conn ); fileCache->offset = 0; } else { /* close local file */ status = close( fileCache->iFd ); fileCache->iFd = 0; fileCache->offset = 0; } UNLOCK_STRUCT( *fileCache ); return status; }
void ifuseDisconnect( iFuseConn_t *tmpIFuseConn ) { LOCK_STRUCT( *tmpIFuseConn ); _ifuseDisconnect( tmpIFuseConn ); UNLOCK_STRUCT( *tmpIFuseConn ); }
int listSize(concurrentList_t *l) { LOCK_STRUCT(*l); int s = _listSize(l); UNLOCK_STRUCT(*l); return s; }
void fl_removeFromConcurrentList2(fl_concurrentList_t *l, void *v) { LOCK_STRUCT(*l); fl_listRemove2(l->list, v); UNLOCK_STRUCT(*l); }
void removeFromConcurrentList(concurrentList_t *l, ListNode *v) { LOCK_STRUCT(*l); listRemoveNoRegion(l->list, v); UNLOCK_STRUCT(*l); }
void removeFromConcurrentList2(concurrentList_t *l, void *v) { LOCK_STRUCT(*l); listRemoveNoRegion2(l->list, v); UNLOCK_STRUCT(*l); }
/*#define FUSE_DEBUG 0*/ void addToConcurrentList(concurrentList_t *l, void *v) { LOCK_STRUCT(*l); listAppendNoRegion(l->list, v); UNLOCK_STRUCT(*l); }
int ifuseFileCacheRead( fileCache_t *fileCache, char *buf, size_t size, off_t offset ) { LOCK_STRUCT( *fileCache ); int status = _ifuseFileCacheRead( fileCache, buf, size, offset ); UNLOCK_STRUCT( *fileCache ); return status; }
int iFuseFileCacheLseek( fileCache_t *fileCache, off_t offset ) { LOCK_STRUCT( *fileCache ); int status = _iFuseFileCacheLseek( fileCache, offset ); UNLOCK_STRUCT( *fileCache ); return status; }
int ifuseFileCacheSwapOut( fileCache_t *fileCache ) { int status = 0; if ( fileCache == NULL ) { return USER__NULL_INPUT_ERR; } /* flush local cache file to remote server */ LOCK_STRUCT( *fileCache ); /* simply return if no file cache or the file cache hasn't been updated */ if ( fileCache->state != HAVE_NEWLY_CREATED_CACHE ) { UNLOCK_STRUCT( *fileCache ); return 0; } /* no need to flush cache file as we are using low-level io */ /* status = fsync (fileCache->iFd); if (status < 0) { status = (errno ? (-1 * errno) : -1); rodsLog (LOG_ERROR, "ifuseFlush: flush of cache file for %s error, status = %d", fileCache->localPath, status); return -EBADF; }*/ struct stat stbuf; int error_code = stat( fileCache->fileCachePath, &stbuf ); if ( error_code != 0 ) { rodsLog( LOG_ERROR, "stat failed in _iFuseFileCacheFlush with status %d", error_code ); } /* put cache file to server */ iFuseConn_t *conn = getAndUseConnByPath( fileCache->localPath, &status ); RECONNECT_IF_NECESSARY( status, conn, ifusePut( conn->conn, fileCache->objPath, fileCache->fileCachePath, fileCache->mode, stbuf.st_size ) ); unuseIFuseConn( conn ); if ( status < 0 ) { rodsLog( LOG_ERROR, "ifuseClose: ifusePut of %s error, status = %d", fileCache->localPath, status ); status = -EBADF; UNLOCK_STRUCT( *fileCache ); return status; } int objFd = status; /* close cache file */ status = close( fileCache->iFd ); if ( status < 0 ) { status = ( errno ? ( -1 * errno ) : -1 ); rodsLog( LOG_ERROR, "ifuseClose: close of cache file for %s error, status = %d", fileCache->localPath, status ); UNLOCK_STRUCT( *fileCache ); return -EBADF; } fileCache->iFd = objFd; fileCache->state = NO_FILE_CACHE; return status; }
void connManager() { time_t curTime; iFuseConn_t *tmpIFuseConn; ListNode *node; List *TimeOutList = newListNoRegion(); while ( 1 ) { curTime = time( NULL ); /* exceed high water mark for number of connection ? */ if ( listSize( ConnectedConn ) > HIGH_NUM_CONN ) { LOCK_STRUCT( *ConnectedConn ); int disconnTarget = _listSize( ConnectedConn ) - HIGH_NUM_CONN; node = ConnectedConn->list->head; while ( node != NULL ) { tmpIFuseConn = ( iFuseConn_t * ) node->value; if ( tmpIFuseConn->inuseCnt == 0 && tmpIFuseConn->pendingCnt == 0 && curTime - tmpIFuseConn->actTime > IFUSE_CONN_TIMEOUT ) { listAppendNoRegion( TimeOutList, tmpIFuseConn ); } node = node->next; } UNLOCK_STRUCT( *ConnectedConn ); node = TimeOutList->head; int disconned = 0; while ( node != NULL && disconned < disconnTarget ) { tmpIFuseConn = ( iFuseConn_t * ) node->value; LOCK_STRUCT( *tmpIFuseConn ); if ( tmpIFuseConn->inuseCnt == 0 && tmpIFuseConn->pendingCnt == 0 && curTime - tmpIFuseConn->actTime > IFUSE_CONN_TIMEOUT ) { removeFromConcurrentList2( FreeConn, tmpIFuseConn ); removeFromConcurrentList2( ConnectedConn, tmpIFuseConn ); if ( tmpIFuseConn->status == 0 ) { /* no struct is referring to it, we can unlock it and free it */ UNLOCK_STRUCT( *tmpIFuseConn ); /* rodsLog(LOG_ERROR, "[FREE IFUSE CONN] %s:%d %p", __FILE__, __LINE__, tmpIFuseConn); */ _freeIFuseConn( tmpIFuseConn ); } else { /* set to timed out */ _ifuseDisconnect( tmpIFuseConn ); UNLOCK_STRUCT( *tmpIFuseConn ); } disconned ++; } else { UNLOCK_STRUCT( *tmpIFuseConn ); } node = node->next; } clearListNoRegion( TimeOutList ); } while ( listSize( ConnectedConn ) <= MAX_NUM_CONN || listSize( FreeConn ) != 0 ) { /* signal one in the wait queue */ connReqWait_t *myConnReqWait = ( connReqWait_t * ) removeFirstElementOfConcurrentList( ConnReqWaitQue ); /* if there is no conn req left, exit loop */ if ( myConnReqWait == NULL ) { break; } myConnReqWait->state = 1; notifyTimeoutWait( &myConnReqWait->mutex, &myConnReqWait->cond ); } #if 0 rodsSleep( CONN_MANAGER_SLEEP_TIME, 0 ); #else timeoutWait( &ConnManagerLock, &ConnManagerCond, CONN_MANAGER_SLEEP_TIME ); #endif } deleteListNoRegion( TimeOutList ); }