| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ***************************************************************************** |
| * |
| * File: CmpConnection.C |
| * Description: The Ipc classes for arkcmp communicating with executor. |
| * The implementaion of ExCmpMessage, CmpIpcEnvironment and |
| * CmpGuaControlConnection classes |
| * |
| * Created: 09/05/96 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ***************************************************************************** |
| */ |
| |
| #define SQLPARSERGLOBALS_NADEFAULTS // should precede all other #include's |
| #define SQLPARSERGLOBALS_FLAGS |
| #define SQLPARSERGLOBALS_NADEFAULTS_SET |
| #include "SqlParserGlobals.h" |
| #include "SqlParserGlobalsCmn.h" |
| |
| #include <iostream> |
| #include "Ipc.h" |
| #include "CmpCommon.h" |
| #include "CmpConnection.h" |
| #include "CmpMessage.h" |
| #include "CmpStatement.h" |
| #include "CmpErrLog.h" |
| #include "NewDel.h" |
| #include "opt.h" |
| #include "NAExit.h" |
| #include "QCache.h" |
| #include "CompException.h" |
| #include "CostMethod.h" |
| #include "NAExecTrans.h" |
| |
| extern THREAD_P jmp_buf ExportJmpBuf; |
| extern THREAD_P jmp_buf CmpInternalErrorJmpBuf; |
| |
| // This is a global variable used per process to identify whether this |
| // arkcmp process is spawned for internal stored procedure execution. |
| // This value is set before the CmpContext is instantiated, so it can't |
| // be put into CmpContext class. |
| THREAD_P NABoolean CmpMainISPConnection = FALSE; |
| |
| ostream &operator<<(ostream &dest, const ComDiagsArea& da); |
| |
| void TODOEMSABORT( const char* msg ) |
| { |
| // TODOEMSABORT is to be replaced later when arkcmp provides EMS support. |
| // It is only used in main program or CmpConnection when the IPC |
| // mechanism is not setup yet. At this period, if there is need to |
| // abort the program, some error reporting will be done. |
| // Once IPC is setup, CMPABORT should be used if there is need to |
| // abort the program. The errors will be sent back to executor and |
| // arkcmp exits gracefully. |
| |
| // This should be implemented in the future to report error log to |
| // EMS, since the IPC mechanism is broken in this kind of error, |
| // there is no way for arkcmp to return error information back to |
| // executor through ComDiags. Currently, arkcmp just exit. |
| cerr << msg << endl; |
| } |
| |
| // This is a last resort, stuff will still scroll past if |
| // an error occurs during UNATTENDED compilation, |
| // when no alert user to right there eyeballing the screen |
| // This function is only necessary if arkcmp is executing |
| // in its own window, i.e. not from a cmd shell. |
| void ArkcmpDelayExit() |
| { |
| #define DEFAULT_DELAY_SECS 10 |
| ULng32 secs = DEFAULT_DELAY_SECS; |
| char *ev = getenv("SQL_ERROR_SLEEP"); // scripts: set to 0 (off), 60, etc |
| if (ev) |
| { |
| char *tmp; |
| secs = strtoul(ev, &tmp, 10/*decimal*/); |
| if (tmp == ev) secs = DEFAULT_DELAY_SECS; // bad env var value |
| } |
| while (secs--) Sleep(1000); |
| } |
| |
| // myNAExit(status) does mxcmp finalization before calling NAExit(status) |
| static void myNAExit(Int32 status) |
| { |
| CURRENTQCACHE->finalize("Dynamic "); |
| NAExit(status); |
| } |
| |
| void ArkcmpErrorMessageBox(const char *msg, |
| ArkcmpErrorSeverity sev, |
| NABoolean doExit, |
| NABoolean doDelay, |
| NABoolean doCerr) |
| { |
| if (doCerr) cerr << msg << endl << flush; |
| |
| // This functionality is only necessary if arkcmp is executing |
| // in its own window, i.e. not from a cmd shell. |
| // if (##running in our own window, not from a cmd shell##) |
| // { |
| |
| static Int32 recursing = 0; |
| // We will popup a msgbox if not out of memory |
| NABoolean doMsgbox = (sev != NOMEM_SEV && !recursing); |
| recursing++; |
| |
| if (doMsgbox) |
| { |
| // Scripts (e.g. runregr, sqlit) set this env var to 0 (meaning off), |
| // which is the default! |
| char *ev = getenv("SQL_ERROR_MSGBOX"); |
| if (!ev || ev[0] == '0') |
| doMsgbox = FALSE; |
| } |
| |
| if (doMsgbox) |
| { |
| UINT mbflags = MB_ICONERROR; |
| if (sev == WARNING_SEV) mbflags = MB_ICONWARNING; |
| else if (sev == INFO_SEV) mbflags = MB_ICONINFORMATION; |
| mbflags |= MB_OK | MB_SETFOREGROUND | MB_TOPMOST; |
| MessageBox(NULL, msg, "tdm_arkcmp", mbflags); |
| } |
| |
| else if (doDelay && recursing < 3) |
| ArkcmpDelayExit(); |
| |
| recursing--; |
| // } // ##running in own window, not invoked from shell |
| |
| if (doExit) |
| { |
| TODOEMSABORT(msg); |
| myNAExit(1); |
| } |
| } |
| |
| |
| // stmtNewHandler_CharSave and stmtNewHandler are used in error |
| // handling when running out of virtual memory happens after the |
| // request is received from executor. |
| |
| // Save 5K bytes of memory for the error handling when running out |
| // of virtual memory, so the error messages can be sent back to executor. |
| static char* stmtNewHandler_CharSave = new char[5120]; |
| |
| |
| static void stmtNewHandler() |
| { |
| delete[] stmtNewHandler_CharSave; |
| // There are some components are catching all exceptions, e.g. |
| // optimizer is catching all exceptions including CMPABORT. |
| // To avoid deleting the stmtNewHandler_CharSave more than once, |
| // it is set to null after deletion. |
| stmtNewHandler_CharSave=0; |
| |
| // Log this error to the file indicated by CMP_ERR_LOG_FILE CQD. |
| CmpErrLog("Memory allocation failure"); |
| |
| if ( CmpCommon::context() ) |
| { |
| *(CmpCommon::diags() ) << DgSqlCode(arkcmpErrorOutOfMemory); |
| CMPABORT; |
| } |
| else |
| { |
| // Must be in the ConnectionType request, the cmpContext is not setup yet; |
| TODOEMSABORT("Run out of virtual memory"); |
| myNAExit(1); |
| } |
| //return 0; |
| } |
| // ----------------------------------------------------------------------- |
| // Methods for ExCmpMessage |
| // ----------------------------------------------------------------------- |
| |
| ExCmpMessage::ExCmpMessage(IpcEnvironment* ipcEnv) : |
| IpcMessageStream(ipcEnv, CmpMessageObj::EXE_CMP_MESSAGE, |
| EXECMPIPCVERSION, 0, TRUE) |
| { |
| endOfConnection_ = FALSE; |
| cmpContext_ = 0; |
| } |
| |
| ExCmpMessage::~ExCmpMessage() |
| { |
| } |
| |
| void ExCmpMessage::setCmpContext(CmpContext* cmpContext) |
| { |
| cmpContext_ = cmpContext; |
| } |
| |
| |
| inline static void receiveAndSetUp(ExCmpMessage *from, CmpMessageObj &to) |
| { |
| *from >> to; |
| Set_SqlParser_Flags(to.getFlags() LAND IPC_COPIABLE_MASK); |
| } |
| |
| inline static void clearAndReset(ExCmpMessage *from) |
| { |
| from->clearAllObjects(); |
| Set_SqlParser_Flags(0); |
| |
| // set DEFAULT_CHARSET to the original CHARSET. It may have been |
| // changed if an internal query with ISO_MAPPING was sent. |
| if (SqlParser_NADefaults_Glob) |
| SetSqlParser_DEFAULT_CHARSET(SqlParser_ORIG_DEFAULT_CHARSET); |
| } |
| |
| static void sendErrorOnLongJump(CmpStatement* cmpStatement, ExCmpMessage* msg, Int32 errcode, CollHeap* heap) |
| { |
| if (cmpStatement) |
| { |
| cmpStatement->exceptionRaised(); |
| if (errcode == MEMALLOC_FAILURE) |
| { |
| // Log this error to the file indicated by CMP_ERR_LOG_FILE CQD. |
| CmpErrLog("Memory allocation failure"); |
| *cmpStatement->diags() << DgSqlCode(arkcmpErrorOutOfMemory); |
| } |
| else |
| { |
| *cmpStatement->diags() << DgSqlCode(arkcmpErrorAssert); |
| *cmpStatement->diags() << DgString0("from longjmp(NAAssert)") |
| << DgString1(__FILE__) << DgInt0(__LINE__); |
| } |
| *msg << *cmpStatement->diags(); |
| if (cmpStatement->reply()) |
| *msg << *cmpStatement->reply(); |
| } |
| else |
| { |
| ComDiagsArea *diags = ComDiagsArea::allocate(heap); |
| *diags << DgSqlCode(arkcmpErrorAssert) |
| << DgString0("from longjmp(NAAssert)") |
| << DgString1(__FILE__) << DgInt0(__LINE__); |
| *msg << *diags; |
| diags->decrRefCount(); |
| } |
| |
| CmpMessageLast last_message; |
| *msg << last_message; |
| msg->send(); |
| } |
| |
| extern THREAD_P jmp_buf CmpInternalErrorJmpBuf; |
| |
| void ExCmpMessage::actOnReceive(IpcConnection* ) |
| { |
| |
| if (getState()==ERROR_STATE) |
| { |
| ArkcmpFatalError(ARKCMP_ERROR_PREFIX |
| "ExCmpMessage::actOnReceive, error from receiving message."); |
| } |
| |
| CmpStatement* volatile cmpStatement=0; |
| CollHeap * volatile ipcHeap = environment_->getHeap(); |
| jmp_buf oldBuf; |
| memcpy (&oldBuf, ExportJmpBufPtr, sizeof(jmp_buf)); |
| |
| |
| Int32 jRc = setjmp(ExportJmpBuf); |
| if (jRc) |
| { |
| // The jmp_buf has to be set back to the old one here, |
| // so if there is an NAAssert in the IPC routines, it will |
| // jump back to the one in main program to avoid infinite loop. |
| memcpy( ExportJmpBufPtr, &oldBuf, sizeof(jmp_buf)); |
| clearAndReset(this); |
| CostMethod::cleanUpAllCostMethods(); |
| sendErrorOnLongJump(cmpStatement, this, jRc, ipcHeap); |
| if ( cmpStatement && cmpStatement->readyToDie() ) |
| delete cmpStatement; |
| return; |
| } |
| |
| ExportJmpBufPtr = &ExportJmpBuf; |
| |
| jmp_buf oldBuf2; |
| memcpy (&oldBuf2, &CmpInternalErrorJmpBuf, sizeof(jmp_buf)); |
| Int32 jRc2 = setjmp(CmpInternalErrorJmpBuf); |
| if(jRc2) |
| { |
| clearAndReset(this); |
| if (stmtNewHandler_CharSave) |
| { |
| delete stmtNewHandler_CharSave; |
| stmtNewHandler_CharSave = 0; |
| } |
| CmpCommon::context()->freeReservedMemory(); |
| CostMethod::cleanUpAllCostMethods(); |
| sendErrorOnLongJump(cmpStatement, this, jRc2, ipcHeap); |
| if (jRc2 == MEMALLOC_FAILURE) |
| ArkcmpFatalError(ARKCMP_ERROR_PREFIX "Out of virtual memory.", NOMEM_SEV); |
| else |
| ArkcmpFatalError(ARKCMP_ERROR_PREFIX "Fatal error from longjmp"); |
| } |
| |
| CmpInternalErrorJmpBufPtr = &CmpInternalErrorJmpBuf; |
| |
| NewHandler_NSK newHandler(stmtNewHandler); |
| |
| try |
| { |
| IpcMessageObjType typ; |
| // For the requests with the following message type the parent qid may not be passed |
| // CmpMessageDescribe |
| // CmpMessageUpdateHist |
| // CmpMessageSetTrans |
| // CmpMessageEndSession |
| // Reset the parent qid and the requests that has parent qid will set it later |
| if (CmpCommon::context() && CmpCommon::context()->sqlSession()) |
| CmpCommon::context()->sqlSession()->setParentQid(NULL); |
| |
| switch (typ=getNextObjType()) |
| { |
| case CmpMessageObj::SQLTEXT_RECOMPILE : |
| case CmpMessageObj::SQLTEXT_COMPILE : |
| { |
| // The number of NAMemory objects that reside in SYSTEM_MEMORY |
| // is currently restricted to one at a time--see explanation in |
| // NAMemory.h. |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageSQLText sqltext(NULL,0,CTXTHEAP,SQLCHARSETCODE_UNKNOWN, |
| (CmpMessageObj::MessageTypeEnum)typ); |
| receiveAndSetUp(this, sqltext); |
| cmpStatement->process(sqltext); |
| break; |
| } |
| |
| case CmpMessageObj::SQLTEXT_STATIC_RECOMPILE : |
| case CmpMessageObj::SQLTEXT_STATIC_COMPILE : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageCompileStmt compilestmt(NULL, |
| 0, |
| (CmpMessageObj::MessageTypeEnum)typ, |
| CTXTHEAP) ; |
| receiveAndSetUp(this, compilestmt); |
| cmpStatement->process(compilestmt); |
| break; |
| } |
| break; |
| |
| case (CmpMessageObj::EXIT_CONNECTION) : |
| setEnd(); |
| break; |
| |
| case (CmpMessageObj::ENVS_REFRESH) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| |
| CmpMessageEnvs env_message(CmpMessageEnvs::NONE, NULL, TRUE, CTXTHEAP); |
| receiveAndSetUp(this, env_message); |
| cmpStatement->process(env_message); |
| CmpCommon::context()->setSecondaryMxcmp(); |
| break; |
| } // end of case (CmpMessageObj::ENVS_REFRESH) |
| |
| case (CmpMessageObj::DDL) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageDDL statement(NULL, 0, CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::DDL) |
| |
| case (CmpMessageObj::DESCRIBE) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageDescribe statement(NULL, 0, CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::DESCRIBE) |
| |
| case (CmpMessageObj::UPDATE_HIST_STAT) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageUpdateHist statement(NULL, 0, CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::UPDATE_HIST_STAT) |
| |
| case (CmpMessageObj::SET_TRANS) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageSetTrans statement(NULL,0,CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::SET_TRANS) |
| |
| case (CmpMessageObj::DATABASE_USER): |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageDatabaseUser statement(NULL, 0, CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::DATABASE_USER) |
| |
| case (CmpMessageObj::DDL_WITH_STATUS) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageDDLwithStatus statement(NULL, 0, CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::DDL_WITH_STATUS) |
| |
| case (CmpMessageObj::END_SESSION) : |
| { |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CmpMessageEndSession statement(NULL,0,CTXTHEAP); |
| receiveAndSetUp(this, statement); |
| cmpStatement->process(statement); |
| break; |
| } // end of case (CmpMessageObj::END_SESSION) |
| |
| case (CmpMessageObj::INTERNALSP_REQUEST) : |
| { |
| CmpStatementISP* ispStatement = new CTXTHEAP CmpStatementISP(cmpContext_); |
| cmpStatement = ispStatement; |
| // The request is dynamically allocated here instead of using |
| // stack variable, because the contents of this request will be |
| // referenced later in INTERNALSP_GETNEXT request for performance |
| // ( so we can avoid some data movement ). CmpStatementISP |
| // owns the CmpISPDateObject which in turns own the |
| // CmpMessageISPRequest. it should be |
| // deleted in the destructor of CmpMessageISPRequest. |
| CmpMessageISPRequest* ispRequest = new CmpMessageISPRequest(); |
| receiveAndSetUp(this, *ispRequest); |
| ispStatement->process(*ispRequest); |
| break; |
| }// end of case(CmpMessageObj::INTERNAL_REQUEST) |
| |
| case (CmpMessageObj::INTERNALSP_GETNEXT) : |
| { |
| CmpMessageISPGetNext ispGetNext; |
| receiveAndSetUp(this, ispGetNext); |
| CmpStatementISP* ispStatement = |
| getISPStatement(ispGetNext.ispRequest()); |
| if (!ispStatement) |
| { |
| // There must be a previous ispStatement, otherwise it is an |
| // internal error. Instantiate a dummy CmpStatement here, just |
| // to place the error information. |
| cmpStatement = new CTXTHEAP CmpStatement(cmpContext_); |
| CMPASSERT(FALSE); |
| } |
| cmpStatement = ispStatement; |
| cmpContext_->setCurrentStatement(cmpStatement); |
| ispStatement->process(ispGetNext); |
| break; |
| } |
| case (CmpMessageObj::CONNECTION_TYPE) : |
| { |
| CmpMessageConnectionType connectionType; |
| receiveAndSetUp(this, connectionType); |
| CmpMainISPConnection = |
| (connectionType.connectionType()==CmpMessageConnectionType::ISP); |
| break; |
| } |
| default: |
| break; |
| } // end of switch (message1.getNextObjType()) |
| clearAndReset(this); |
| } |
| catch(CmpInternalException& exInternal) |
| { |
| clearAndReset(this); |
| if (cmpStatement) |
| { |
| cmpStatement->error(arkcmpErrorAssert, exInternal.getMsg()); |
| cmpStatement->exceptionRaised(); |
| } |
| else |
| { |
| ComDiagsArea *diags = ComDiagsArea::allocate(ipcHeap); |
| *diags << DgSqlCode(arkcmpErrorAssert) |
| << DgInt0(0) << DgString0("from CMPASSERT") |
| << DgString1("CmpConnection::actOnReceive,EH_INTERNAL_EXCEPTION"); |
| *this << *diags; |
| diags->decrRefCount(); |
| } |
| } |
| catch(EHBreakException & x) |
| { |
| clearAndReset(this); |
| if ( !CmpCommon::diags()->getNumber() ) |
| *CmpCommon::diags() << DgSqlCode(arkcmpErrorAssert) << DgString0("EHBreakException") << DgString1(x.getFileName()) << DgInt0(x.getLineNum()); |
| cerr << *CmpCommon::diags(); |
| *this << *CmpCommon::diags(); |
| CmpMessageLast last_message; |
| *this << last_message; |
| send(); |
| ArkcmpFatalError(ARKCMP_ERROR_PREFIX "actOnReceive exception."); |
| } |
| catch(...) |
| { |
| clearAndReset(this); |
| if (cmpStatement) |
| { |
| cmpStatement->error(arkcmpErrorNoDiags, "Unknown Exception"); |
| cmpStatement->exceptionRaised(); |
| } |
| else |
| { |
| ComDiagsArea *diags = ComDiagsArea::allocate(ipcHeap); |
| *diags << DgSqlCode(arkcmpErrorAssert) |
| << DgString0("from longjmp(CmpConnection::actOnReceive)") |
| << DgString1(__FILE__) << DgInt0(__LINE__); |
| *this << *diags; |
| diags->decrRefCount(); |
| } |
| } |
| |
| // reset the Buf back to the original one. |
| memcpy (ExportJmpBufPtr, &oldBuf, sizeof(jmp_buf)); |
| memcpy (&CmpInternalErrorJmpBuf, &oldBuf, sizeof(jmp_buf)); |
| clearAndReset(this); |
| |
| if (cmpStatement) |
| { |
| ComDiagsArea *diags = cmpStatement->diags(); |
| if (diags->getNumber() > 0) |
| *this << *cmpStatement->diags(); |
| if (cmpStatement->reply()) |
| *this << *cmpStatement->reply(); |
| } |
| |
| CmpMessageLast last_message; |
| *this << last_message; |
| |
| send(); |
| |
| if ( cmpStatement && cmpStatement->readyToDie() ) |
| delete cmpStatement; |
| |
| if ( !stmtNewHandler_CharSave ) |
| // Some components (e.g. optimizer) might catch all exceptions and |
| // handle errors differently. In the case of running out of virtual |
| // memory error, the program should exit right after sending the reply. |
| myNAExit(-1); |
| } |
| |
| void ExCmpMessage::actOnSend(IpcConnection* ) |
| |
| { |
| if (getState() == ERROR_STATE) |
| { |
| ArkcmpFatalError(ARKCMP_ERROR_PREFIX |
| "Error from sending reply back to executor."); |
| } |
| |
| } |
| |
| CmpStatementISP* ExCmpMessage::getISPStatement(Int64 id) |
| { |
| NAList<CmpStatement*> statements = cmpContext_->statements(); |
| CmpStatementISP* ispStatement; |
| for ( CollIndex i = 0; i < statements.entries(); i++) |
| if ( statements[i] && ( ispStatement = (statements[i])->ISPStatement() ) |
| && ispStatement->ISPReqId() == id ) |
| return ispStatement; |
| return 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Methods for CmpIpcEnvironment |
| // ----------------------------------------------------------------------- |
| |
| void CmpIpcEnvironment::initControl(IpcServerAllocationMethod allocMethod, |
| Int32 sockArg, |
| Int32 portArg) |
| { |
| switch (allocMethod) |
| { |
| case IPC_LAUNCH_GUARDIAN_PROCESS: |
| case IPC_SPAWN_OSS_PROCESS: |
| { |
| GuaReceiveControlConnection *cc = |
| new(this) CmpGuaControlConnection(this); |
| setControlConnection(cc); |
| cc->waitForMaster(); |
| break; |
| } |
| case IPC_INETD: |
| case IPC_POSIX_FORK_EXEC: |
| setControlConnection(new (this) SockControlConnection(this)); |
| break; |
| // copied from ex_esp_main.C ( 03/18/97 ) |
| case IPC_LAUNCH_NT_PROCESS: |
| break; |
| default : |
| { |
| ArkcmpFatalError(ARKCMP_ERROR_PREFIX "Invalid connection method."); |
| } |
| break; |
| } |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Implementation of CmpGuaControlConnection, handles system messages |
| // ----------------------------------------------------------------------- |
| |
| |
| CmpGuaControlConnection::CmpGuaControlConnection(IpcEnvironment* env, |
| short receiveDepth) : |
| GuaReceiveControlConnection(env, receiveDepth) |
| { |
| } |
| |
| CmpGuaControlConnection::~CmpGuaControlConnection() |
| { |
| } |
| |
| |