#ifdef RCSID static char *RCSid = "$Header: Z:\rdbms\demo\cdemomt.c@@\main\nt_8.0.4\1 \ Checked in on Thu Jul 24 14:02:55 1997 by snakai \ Copyright (c) 1997 by Oracle Corporation. All Rights Reserved. \ $"; #endif /* RCSID */ /* Copyright (c) Oracle Corporation 1997. All Rights Reserved. */ /* NAME cdemomt.c DESCRIPTION Demonstrates OCI multithreading on the Windows NT platform. PUBLIC FUNCTION(S) (none) PRIVATE FUNCTION(S) fnThreadFunction() - Thread function that adds employees to EMP table. checkerr() - Checks return values for errors. do_exit() - Frees handles and exits program. RETURNS (n/a) NOTES This program requires the EMP table from the default database. You can verify the results by querying the table manually as SCOTT/TIGER. MODIFIED (MM/DD/YY) ekhor 06/12/98 - Use OCIBindByPos, OCIBindByName stmar 05/15/97 - Multithreading conversion [CDemoMT] echen 12/17/96 - OCI beautification [CDemo81] dchatter 07/18/96 - delete spurious header files dchatter 07/15/96 - hda is a ub4 array to prevent bus error mgianata 06/17/96 - change ociisc() to OCISessionBegin() aroy 04/26/96 - change OCITransCommitt -> OCITransCommit slari 04/24/96 - use OCITransCommitt aroy 02/21/96 - fix bug in get descriptor handle call lchidamb 02/20/96 - cdemo81.c converted for v8 OCI lchidamb 02/20/96 - Creation */ /* * -- cdemomt.c -- * * This program spawns two simultaneous threads that attempt to insert * different employee names with the same ID numbers. Automatic mutexing * is demonstrated. * * If an employee is inserted successfully, the following is printed: * "Thread n: ENAME added as employee xxxx." * * Otherwise, the following is printed: * "Thread n: ENAME not added, employee xxxx already exists." * * You'll probably want to manually delete the records inserted by this demo * after each run. Remember to commit the delete. * * This program is based on CDemo81.c, a non-multithreaded demo of SQL * processing. Most of the SQL functionality of CDemo81 has been removed to * clarify the multithreading functionality of CDemoMT. * * More information on OCI multithreading can be found in Chapter 5 of the * OCI reference manual. This program demonstrates the use of automatic * mutexing under scenario 2, multiple threads under one connection. */ #include #include #include #include #include #include #include #include #define HOST "" #define ENAMELEN 10 /* Define structure used to pass info to spawned threads */ typedef struct _STHREADINFO { DWORD dwThreadNum; DWORD dwThreadID; } STHREADINFO; /* Global OCI handles */ OCIEnv *envhp = NULL; OCIServer *srvhp = NULL; OCISvcCtx *svchp = NULL; OCIError *errhp = NULL; /* Function declarations */ static DWORD WINAPI fnThreadFunction(LPVOID lpvThreadArg); static void checkerr(OCIError *errhp, sword status); static void do_exit(sword exit_code); /* * Main function allocates shared OCI handles and spawns two worker threads. */ void main(void) { /* Define username and password for EMP table */ text *username = (text *) "SCOTT"; text *password = (text *) "TIGER"; STHREADINFO sThreadInfo[2]; HANDLE hThreads[2]; OCISession *authp = (OCISession *) NULL; BOOL bDone = FALSE; DWORD i; dvoid *tmp; if (!sThreadInfo || !hThreads) { printf("Unable to allocate STHREADINFO structure.\n"); do_exit(EXIT_FAILURE); } /* Initialize shared OCI handles */ (void) OCIInitialize(OCI_THREADED, (dvoid *) 0, 0, 0, 0); (void) OCIEnvInit((OCIEnv **) &envhp, OCI_DEFAULT, 21, (dvoid **) &tmp ); (void) OCIHandleAlloc((dvoid *) envhp, (dvoid **) &errhp, OCI_HTYPE_ERROR, 33, (dvoid **) &tmp); (void) OCIHandleAlloc((dvoid *) envhp, (dvoid **) &srvhp, OCI_HTYPE_SERVER, 52, (dvoid **) &tmp); (void) OCIHandleAlloc((dvoid *) envhp, (dvoid **) &svchp, OCI_HTYPE_SVCCTX, 55, (dvoid **) &tmp); (void) OCIServerAttach(srvhp, errhp, (text *) HOST, strlen(HOST), 0); /* Set attribute server context in the service context */ (void) OCIAttrSet((dvoid *) svchp, OCI_HTYPE_SVCCTX, srvhp, (ub4) 0, OCI_ATTR_SERVER, (OCIError *) errhp); (void) OCIHandleAlloc((dvoid *) envhp, (dvoid **) &authp, (ub4) OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0); (void) OCIAttrSet((dvoid *) authp, (ub4) OCI_HTYPE_SESSION, (dvoid *) username, (ub4) strlen(username), OCI_ATTR_USERNAME, errhp); (void) OCIAttrSet((dvoid *) authp, (ub4) OCI_HTYPE_SESSION, (dvoid *) password, (ub4) strlen(password), OCI_ATTR_PASSWORD, errhp); checkerr(errhp, OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, (ub4) OCI_DEFAULT)); (void) OCIAttrSet((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX, (dvoid *) authp, (ub4) 0, (ub4) OCI_ATTR_SESSION, errhp); /* Spawn the two worker threads */ for (i = 0; i < 2; i++) { (sThreadInfo[i]).dwThreadNum = i; hThreads[i] = CreateThread( NULL, /* security */ (DWORD)0, /* stack size */ fnThreadFunction, /* function ptr */ (LPVOID) &(sThreadInfo[i]), /* arg struct */ 0, /* creation flags */ &((sThreadInfo[i]).dwThreadID)); /* thread ID */ if (!(hThreads[i])) printf("Error creating thread %i.\n", i); else printf("Thread %i created with ID = %i.\n", i, (sThreadInfo[i]).dwThreadID); } /* Wait for threads to finish */ while (!bDone) { DWORD dwWaitResult = WaitForMultipleObjects(2, hThreads, TRUE, 1000); switch (dwWaitResult) { case WAIT_TIMEOUT: break; case WAIT_FAILED: printf("WaitMultiple Error.\n"); break; default: bDone = TRUE; break; } } for (i = 0; i < 2; i++) CloseHandle(hThreads[i]); printf("Exiting.\n"); do_exit(EXIT_SUCCESS); } /* * Thread function inserts data into table. In this demo, both threads call * the same entry function. */ DWORD WINAPI fnThreadFunction(LPVOID lpvThreadArg) { /* Define INSERT statement */ text *insert = (text *) "INSERT INTO emp(empno, ename) VALUES (:empno, :ename)"; STHREADINFO *psInfo = (STHREADINFO *) lpvThreadArg; OCIStmt *stmthp = NULL; OCIBind *bnd1p = NULL; OCIBind *bnd2p = NULL; text ename[ENAMELEN+2]; char loopn[2]; sword empno; sb4 status; dvoid *tmp; DWORD i; /* Allocate statement, bind, and define handles */ checkerr(errhp, OCIHandleAlloc((dvoid *) envhp, (dvoid **) &stmthp, OCI_HTYPE_STMT, 100, (dvoid **) &tmp)); checkerr(errhp, OCIStmtPrepare(stmthp, errhp, (text *) insert, (ub4) strlen(insert), OCI_NTV_SYNTAX, OCI_DEFAULT)); /* Bind the placeholders in the INSERT statement */ if ((status = OCIBindByName(stmthp, &bnd1p, errhp, (text *) ":ENAME", -1, (dvoid *) ename, ENAMELEN+1, SQLT_STR, (dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)) || (status = OCIBindByName(stmthp, &bnd2p, errhp, (text *) ":EMPNO", -1, (dvoid *) &empno, (sword) sizeof(empno), SQLT_INT, (dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT))) { checkerr(errhp, status); do_exit(EXIT_FAILURE); } srand(psInfo->dwThreadID); /* Loop through employees. * * Each thread generates ten employee names. Thread 0 uses names "A-10" * through "A-19", while thread 1 uses names "B-10" through "B-19". A single * attempt is made to insert each employee with an ID between 1000 and 1029. * Since the IDs are randomly generated and unique, the number of successes * can change each time the demo is run. */ for (i = 10; i < 20; i++) { /* Generate employee info */ if (psInfo->dwThreadNum == 0) strcpy(ename, "A-"); else strcpy(ename, "B-"); sprintf(loopn, "%i", i); strcat(ename, loopn); /* employee name */ empno = 1000 + (rand() % 30); /* employee ID */ Sleep(rand() % 500); /* simulate user delay */ /* Insert record */ status = OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, OCI_DEFAULT); switch (status) { case 0: break; case -1: printf("Thread %i: %s not added, employee %i already exists.\n", psInfo->dwThreadNum, ename, empno); continue; default: checkerr(errhp, status); do_exit(EXIT_FAILURE); } /* Commit change */ if (status = OCITransCommit(svchp, errhp, 0)) { checkerr(errhp, status); do_exit(EXIT_FAILURE); } printf("Thread %i: %s added as employee %i.\n", psInfo->dwThreadNum, ename, empno); } printf("Thread %i exiting.\n", psInfo->dwThreadNum); ExitThread(1); return 1; /* prevent warning */ } /* * Check for errors. */ static void checkerr(OCIError *errhp, sword status) { text errbuf[512]; sb4 errcode; switch (status) { case OCI_SUCCESS: break; case OCI_SUCCESS_WITH_INFO: (void) printf("Error - OCI_SUCCESS_WITH_INFO\n"); break; case OCI_NEED_DATA: (void) printf("Error - OCI_NEED_DATA\n"); break; case OCI_NO_DATA: (void) printf("Error - OCI_NODATA\n"); break; case OCI_ERROR: (void) OCIErrorGet(errhp, (ub4) 1, (text *) NULL, &errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR); (void) printf("Error - %s\n", errbuf); break; case OCI_INVALID_HANDLE: (void) printf("Error - OCI_INVALID_HANDLE\n"); break; case OCI_STILL_EXECUTING: (void) printf("Error - OCI_STILL_EXECUTE\n"); break; case OCI_CONTINUE: (void) printf("Error - OCI_CONTINUE\n"); break; default: break; } } /* * Exit program with an exit code. */ void do_exit(sword exit_code) { if (errhp) (void) OCIServerDetach(srvhp, errhp, OCI_DEFAULT); if (envhp) (void) OCIHandleFree((dvoid *) errhp, OCI_HTYPE_ENV); exit(exit_code); } /* end of file */