📄 ncbi_connection.c
字号:
/* * =========================================================================== * PRODUCTION $Log: ncbi_connection.c,v $ * PRODUCTION Revision 1000.3 2004/06/01 18:44:48 gouriano * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R6.42 * PRODUCTION * =========================================================================== *//* $Id: ncbi_connection.c,v 1000.3 2004/06/01 18:44:48 gouriano Exp $ * =========================================================================== * * PUBLIC DOMAIN NOTICE * National Center for Biotechnology Information * * This software/database is a "United States Government Work" under the * terms of the United States Copyright Act. It was written as part of * the author's official duties as a United States Government employee and * thus cannot be copyrighted. This software/database is freely available * to the public for use. The National Library of Medicine and the U.S. * Government have not placed any restriction on its use or reproduction. * * Although all reasonable efforts have been taken to ensure the accuracy * and reliability of the software and data, the NLM and the U.S. * Government do not and cannot warrant the performance or results that * may be obtained by using this software or data. The NLM and the U.S. * Government disclaim all warranties, express or implied, including * warranties of performance, merchantability or fitness for any particular * purpose. * * Please cite the author in any work or product based on this material. * * =========================================================================== * * Author: Denis Vakatov, Anton Lavrentiev * * File Description: * Generic API to open and handle connection to an abstract service. * For more detail, see in "ncbi_connection.h". * */#include "ncbi_priv.h"#include <connect/ncbi_buffer.h>#include <connect/ncbi_connection.h>#include <stdlib.h>#include <string.h>/*********************************************************************** * INTERNAL ***********************************************************************//* Standard logging message */#define CONN_LOG_EX(level, descr, status) \ CORE_LOGF(level, \ ("%s (connector \"%s\", error \"%s\")", descr, \ conn->meta.get_type \ ? conn->meta.get_type(conn->meta.c_get_type) \ : "Unknown", IO_StatusStr(status)))#define CONN_LOG(level, descr) CONN_LOG_EX(level, descr, status)/* Standard macros to verify that the passed connection handle is not NULL */#define CONN_NOT_NULL_EX(func_name, status) \ if ( !conn ) { \ CORE_LOG(eLOG_Error, "CONN_" #func_name \ "(conn, ...) -- passed NULL connection handle"); \ assert(conn); \ return status; \ }#define CONN_NOT_NULL(func_name) \ CONN_NOT_NULL_EX(func_name, eIO_InvalidArg)/* Connection state */typedef enum ECONN_StateTag { eCONN_Unusable = -1, /* this should be iff !conn->meta.list*/ eCONN_Closed = 0, eCONN_Open = 1} ECONN_State;/* Connection internal data */typedef struct SConnectionTag { SMetaConnector meta; /* VTable of operations and list */ BUF buf; /* storage for the Peek'd data */#ifdef IMPLEMENTED__CONN_WaitAsync SConnectorAsyncHandler async_data; /* info of curr. async event handler */#endif ECONN_State state; /* connection state */ /* "[c|r|w|l]_timeout" is either 0 (kInfiniteTimeout), kDefaultTimeout (to use connector-specific one), or points to "[cc|rr|ww|ll]_timeout" */ const STimeout* o_timeout; /* timeout on open */ const STimeout* r_timeout; /* timeout on reading */ const STimeout* w_timeout; /* timeout on writing */ const STimeout* c_timeout; /* timeout on close */ STimeout oo_timeout; /* storage for "o_timeout" */ STimeout rr_timeout; /* storage for "r_timeout" */ STimeout ww_timeout; /* storage for "w_timeout" */ STimeout cc_timeout; /* storage for "c_timeout" */ SCONN_Callback cbs[CONN_N_CALLBACKS];} SConnection;/*********************************************************************** * EXTERNAL ***********************************************************************/extern EIO_Status CONN_Create(CONNECTOR connector, CONN* connection){ CONN conn = (SConnection*) calloc(1, sizeof(SConnection)); EIO_Status status = eIO_Unknown; if ( conn ) { conn->state = eCONN_Unusable; conn->o_timeout = kDefaultTimeout; conn->r_timeout = kDefaultTimeout; conn->w_timeout = kDefaultTimeout; conn->c_timeout = kDefaultTimeout; if ((status = CONN_ReInit(conn, connector)) != eIO_Success) { free(conn); conn = 0; } } *connection = conn; return status;}extern EIO_Status CONN_ReInit(CONN conn, CONNECTOR connector){ CONNECTOR x_conn = 0; EIO_Status status; CONN_NOT_NULL(ReInit); /* check arg */ if (!connector && !conn->meta.list) { assert(conn->state == eCONN_Unusable); status = eIO_Unknown; CONN_LOG(eLOG_Error, "[CONN_ReInit] Cannot re-init empty connection with NULL"); return status; } /* reset and close current connector(s), if any */ if ( conn->meta.list ) {#ifdef IMPLEMENTED__CONN_WaitAsync /* cancel async. i/o event handler */ CONN_WaitAsync(conn, eIO_ReadWrite, 0, 0, 0);#endif {{ /* erase unread data */ size_t buf_size = BUF_Size(conn->buf); verify(BUF_Read(conn->buf, 0, buf_size) == buf_size); }} /* call current connector's "FLUSH" and "CLOSE" methods */ if (conn->state == eCONN_Open) { if ( conn->meta.flush ) { conn->meta.flush(conn->meta.c_flush, conn->c_timeout == kDefaultTimeout ? conn->meta.default_timeout : conn->c_timeout); } if ( conn->meta.close ) { status = conn->meta.close(conn->meta.c_close, conn->c_timeout == kDefaultTimeout ? conn->meta.default_timeout : conn->c_timeout); if (status != eIO_Success) { CONN_LOG(connector ? eLOG_Error : eLOG_Warning, "[CONN_ReInit] Cannot close current connection"); if (connector) return status; } } conn->state = eCONN_Closed; } for (x_conn = conn->meta.list; x_conn; x_conn = x_conn->next) { if (x_conn == connector) { /* Reinit with the same and the only connector - allowed */ if (!x_conn->next && x_conn == conn->meta.list) break; status = eIO_Unknown; CONN_LOG(eLOG_Error, "[CONN_ReInit] Partial re-init not allowed"); return status; } } if ( !x_conn ) { /* Entirely new connector - remove the old connector stack first */ METACONN_Remove(&conn->meta, 0); assert(conn->meta.list == 0); memset(&conn->meta, 0, sizeof(conn->meta)); conn->state = eCONN_Unusable; } } if (connector && !x_conn) { assert(conn->state == eCONN_Unusable); /* Setup the new connector */ if (METACONN_Add(&conn->meta, connector) != eIO_Success) return eIO_Unknown; conn->state = eCONN_Closed; } assert(conn->state != eCONN_Open); return eIO_Success;}static EIO_Status s_Open(CONN conn){ EIO_Status status; assert(conn->state == eCONN_Closed && conn->meta.list != 0); /* call current connector's "OPEN" method */ status = conn->meta.open ? conn->meta.open(conn->meta.c_open, conn->o_timeout == kDefaultTimeout ? conn->meta.default_timeout : conn->o_timeout) : eIO_NotSupported; if (status != eIO_Success) { CONN_LOG(eLOG_Error, "[CONN_Open] Cannot open connection"); return status; } /* success */ conn->state = eCONN_Open; return status;}extern const char* CONN_GetType(CONN conn){ CONN_NOT_NULL_EX(GetType, 0); return conn->state == eCONN_Unusable || !conn->meta.list || !conn->meta.get_type ? 0 : conn->meta.get_type(conn->meta.c_get_type);}extern char* CONN_Description(CONN conn){ CONN_NOT_NULL_EX(Description, 0); return conn->state == eCONN_Unusable || !conn->meta.list || !conn->meta.descr ? 0 : conn->meta.descr(conn->meta.c_descr);}extern EIO_Status CONN_SetTimeout(CONN conn, EIO_Event event, const STimeout* new_timeout){ EIO_Status status = eIO_Success; CONN_NOT_NULL(SetTimeout); switch (event) { case eIO_Open: if (new_timeout && new_timeout != kDefaultTimeout) { conn->oo_timeout = *new_timeout; conn->o_timeout = &conn->oo_timeout; } else { conn->o_timeout = new_timeout; } break; case eIO_Close: if (new_timeout && new_timeout != kDefaultTimeout) { conn->cc_timeout = *new_timeout; conn->c_timeout = &conn->cc_timeout; } else { conn->c_timeout = new_timeout; } break; case eIO_Read: case eIO_ReadWrite: if (new_timeout && new_timeout != kDefaultTimeout) { conn->rr_timeout = *new_timeout; conn->r_timeout = &conn->rr_timeout; } else { conn->r_timeout = new_timeout; } if (event != eIO_ReadWrite) break; /*FALLTHRU*/ case eIO_Write: if (new_timeout && new_timeout != kDefaultTimeout) { conn->ww_timeout = *new_timeout; conn->w_timeout = &conn->ww_timeout; } else { conn->w_timeout = new_timeout; } break; default: status = eIO_InvalidArg; CONN_LOG(eLOG_Error, "[CONN_SetTimeout] Unknown event to set timeout for"); assert(0); break; } return status;}extern const STimeout* CONN_GetTimeout(CONN conn, EIO_Event event){ CONN_NOT_NULL_EX(GetTimeout, 0); switch (event) { case eIO_Open: return conn->o_timeout; case eIO_ReadWrite: CONN_LOG_EX(eLOG_Warning, "[CONN_GetTimeout] ReadWrite timeout requested", eIO_InvalidArg); /*FALLTHRU*/ case eIO_Read: return conn->r_timeout; case eIO_Write: return conn->w_timeout; case eIO_Close: return conn->c_timeout; default: CONN_LOG_EX(eLOG_Error, "[CONN_GetTimeout] Unknown event to get timeout for", eIO_InvalidArg); assert(0); break; } return 0;}extern EIO_Status CONN_Wait(CONN conn, EIO_Event event, const STimeout* timeout){ EIO_Status status; CONN_NOT_NULL(Wait); if (conn->state == eCONN_Unusable || (event != eIO_Read && event != eIO_Write) || timeout == kDefaultTimeout) return eIO_InvalidArg; /* perform open, if not opened yet */ if (conn->state != eCONN_Open && (status = s_Open(conn)) != eIO_Success) return status; assert(conn->state == eCONN_Open && conn->meta.list != 0); /* check if there is a PEEK'ed data in the input */ if (event == eIO_Read && BUF_Size(conn->buf)) return eIO_Success; /* call current connector's "WAIT" method */ status = conn->meta.wait ? conn->meta.wait(conn->meta.c_wait, event, timeout) : eIO_NotSupported; if (status != eIO_Success) { if (status != eIO_Timeout) CONN_LOG(eLOG_Error, "[CONN_Wait] Error waiting on I/O"); else if (!timeout || timeout->sec || timeout->usec) CONN_LOG(eLOG_Warning, "[CONN_Wait] I/O timed out"); } return status;}static EIO_Status s_CONN_Write(CONN conn, const void* buf, size_t size, size_t* n_written){ EIO_Status status; assert(*n_written == 0); /* check if the write method is specified at all */ if ( !conn->meta.write ) { status = eIO_NotSupported; CONN_LOG(eLOG_Error, "[CONN_Write] Unable to write data"); return status; } /* call current connector's "WRITE" method */ status = conn->meta.write(conn->meta.c_write, buf, size, n_written, conn->w_timeout==kDefaultTimeout ? conn->meta.default_timeout :conn->w_timeout); if (status != eIO_Success) { if ( *n_written ) { CONN_LOG(eLOG_Trace, "[CONN_Write] Write error"); status = eIO_Success; } else if ( size ) CONN_LOG(eLOG_Error, "[CONN_Write] Cannot write data"); } return status;}static EIO_Status s_CONN_WritePersist(CONN conn, const void* buf, size_t size, size_t* n_written){ EIO_Status status; assert(*n_written == 0); for (;;) { size_t x_written = 0; status = s_CONN_Write(conn, (char*) buf + *n_written, size - *n_written, &x_written); *n_written += x_written; if (*n_written == size || status != eIO_Success) break; } return status;}extern EIO_Status CONN_Write(CONN conn, const void* buf, size_t size, size_t* n_written, EIO_WriteMethod how){ EIO_Status status; if (!n_written) return eIO_InvalidArg; *n_written = 0; if (size && !buf) return eIO_InvalidArg; CONN_NOT_NULL(Write); if (conn->state == eCONN_Unusable) return eIO_InvalidArg; /* open connection, if not yet opened */ if (conn->state != eCONN_Open && (status = s_Open(conn)) != eIO_Success) return status; assert(conn->state == eCONN_Open && conn->meta.list != 0); switch (how) { case eIO_WritePlain: return s_CONN_Write(conn, buf, size, n_written); case eIO_WritePersist: return s_CONN_WritePersist(conn, buf, size, n_written); default: break; } return eIO_Unknown;}extern EIO_Status CONN_PushBack(CONN conn, const void* buf, size_t size){ CONN_NOT_NULL(PushBack); if (conn->state != eCONN_Open) return eIO_InvalidArg; return BUF_PushBack(&conn->buf, buf, size) ? eIO_Success : eIO_Unknown;}extern EIO_Status CONN_Flush(CONN conn){ EIO_Status status;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -