1/*********************************************************************
2 *
3 * This is based on code created by Peter Harvey,
4 * (pharvey@codebydesign.com).
5 *
6 * Modified and extended by Nick Gorham
7 * (nick@lurcher.org).
8 *
9 * Any bugs or problems should be considered the fault of Nick and not
10 * Peter.
11 *
12 * copyright (c) 1999 Nick Gorham
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 **********************************************************************
29 *
30 * $Id: __handles.c,v 1.13 2009/05/15 15:23:56 lurcher Exp $
31 *
32 * $Log: __handles.c,v $
33 * Revision 1.13 2009/05/15 15:23:56 lurcher
34 * Fix pooled connection thread problems
35 *
36 * Revision 1.12 2009/02/18 17:59:08 lurcher
37 * Shift to using config.h, the compile lines were making it hard to spot warnings
38 *
39 * Revision 1.11 2009/02/17 09:47:44 lurcher
40 * Clear up a number of bugs
41 *
42 * Revision 1.10 2007/02/28 15:37:49 lurcher
43 * deal with drivers that call internal W functions and end up in the driver manager. controlled by the --enable-handlemap configure arg
44 *
45 * Revision 1.9 2006/05/31 17:35:34 lurcher
46 * Add unicode ODBCINST entry points
47 *
48 * Revision 1.8 2004/09/28 08:44:46 lurcher
49 * Fix memory leak in pthread descriptor code
50 *
51 * Revision 1.7 2004/07/24 17:55:37 lurcher
52 * Sync up CVS
53 *
54 * Revision 1.6 2003/06/04 12:49:45 lurcher
55 *
56 * Further PID logging tweeks
57 *
58 * Revision 1.5 2003/06/02 16:51:36 lurcher
59 *
60 * Add TracePid option
61 *
62 * Revision 1.4 2002/08/12 16:20:44 lurcher
63 *
64 * Make it try and find a working iconv set of encodings
65 *
66 * Revision 1.3 2002/08/12 13:17:52 lurcher
67 *
68 * Replicate the way the MS DM handles loading of driver libs, and allocating
69 * handles in the driver. usage counting in the driver means that dlopen is
70 * only called for the first use, and dlclose for the last. AllocHandle for
71 * the driver environment is only called for the first time per driver
72 * per application environment.
73 *
74 * Revision 1.2 2002/02/22 10:23:22 lurcher
75 *
76 * s/Trace File/TraceFile
77 *
78 * Revision 1.1.1.1 2001/10/17 16:40:07 lurcher
79 *
80 * First upload to SourceForge
81 *
82 * Revision 1.14 2001/06/25 12:55:15 nick
83 *
84 * Fix threading problem with multiple ENV's
85 *
86 * Revision 1.13 2001/06/04 15:24:49 nick
87 *
88 * Add port to MAC OSX and QT3 changes
89 *
90 * Revision 1.12 2001/05/15 13:33:44 jason
91 *
92 * Wrapped calls to stats with COLLECT_STATS
93 *
94 * Revision 1.11 2001/04/12 17:43:36 nick
95 *
96 * Change logging and added autotest to odbctest
97 *
98 * Revision 1.10 2001/03/02 14:24:23 nick
99 *
100 * Fix thread detection for Solaris
101 *
102 * Revision 1.9 2001/01/04 13:16:25 nick
103 *
104 * Add support for GNU portable threads and tidy up some UNICODE compile
105 * warnings
106 *
107 * Revision 1.8 2000/12/18 11:51:59 martin
108 *
109 * stats specific mode to uodbc_open_stats.
110 *
111 * Revision 1.7 2000/12/18 11:03:58 martin
112 *
113 * Add support for the collection and retrieval of handle statistics.
114 *
115 * Revision 1.6 2000/12/17 11:17:22 nick
116 *
117 * Remove typo
118 *
119 * Revision 1.5 2000/12/17 11:00:32 nick
120 *
121 * Add thread safe bits to pooling
122 *
123 * Revision 1.4 2000/11/29 17:53:59 nick
124 *
125 * Fix race condition
126 *
127 * Revision 1.3 2000/10/25 09:39:42 nick
128 *
129 * Clear handles out, to avoid reuse
130 *
131 * Revision 1.2 2000/09/08 08:58:17 nick
132 *
133 * Add SQL_DRIVER_HDESC to SQLGetinfo
134 *
135 * Revision 1.1.1.1 2000/09/04 16:42:52 nick
136 * Imported Sources
137 *
138 * Revision 1.16 2000/06/29 17:27:52 ngorham
139 *
140 * Add fast validate option
141 *
142 * Revision 1.15 2000/06/27 17:34:12 ngorham
143 *
144 * Fix a problem when the second part of the connect failed a seg fault
145 * was generated in the error reporting
146 *
147 * Revision 1.14 2001/03/28 23:09:57 ngorham
148 *
149 * Fix logging
150 *
151 * Revision 1.13 2000/03/11 15:55:47 ngorham
152 *
153 * A few more changes and bug fixes (see NEWS)
154 *
155 * Revision 1.12 2000/02/25 00:02:00 ngorham
156 *
157 * Add a patch to support IBM DB2, and Solaris threads
158 *
159 * Revision 1.11 2000/02/22 22:14:45 ngorham
160 *
161 * Added support for solaris threads
162 * Added check to overcome bug in PHP4
163 * Fixed bug in descriptors and ODBC 3 drivers
164 *
165 * Revision 1.10 1999/12/11 13:01:57 ngorham
166 *
167 * Add some fixes to the Postgres driver for long types
168 *
169 * Revision 1.9 1999/12/01 09:20:07 ngorham
170 *
171 * Fix some threading problems
172 *
173 * Revision 1.8 1999/11/13 23:41:01 ngorham
174 *
175 * Alter the way DM logging works
176 * Upgrade the Postgres driver to 6.4.6
177 *
178 * Revision 1.7 1999/11/10 03:51:34 ngorham
179 *
180 * Update the error reporting in the DM to enable ODBC 3 and 2 calls to
181 * work at the same time
182 *
183 * Revision 1.6 1999/08/05 18:59:49 ngorham
184 *
185 * Typo error found by Greg Bentz
186 *
187 * Revision 1.5 1999/08/03 21:47:39 shandyb
188 * Moving to automake: changed files in DriverManager
189 *
190 * Revision 1.4 1999/07/10 21:10:17 ngorham
191 *
192 * Adjust error sqlstate from driver manager, depending on requested
193 * version (ODBC2/3)
194 *
195 * Revision 1.3 1999/07/04 21:05:08 ngorham
196 *
197 * Add LGPL Headers to code
198 *
199 * Revision 1.2 1999/06/30 23:56:56 ngorham
200 *
201 * Add initial thread safety code
202 *
203 * Revision 1.1.1.1 1999/05/29 13:41:09 sShandyb
204 * first go at it
205 *
206 * Revision 1.1.1.1 1999/05/27 18:23:18 pharvey
207 * Imported sources
208 *
209 * Revision 1.3 1999/05/09 23:27:11 nick
210 * All the API done now
211 *
212 * Revision 1.2 1999/05/03 19:50:43 nick
213 * Another check point
214 *
215 * Revision 1.1 1999/04/25 23:06:11 nick
216 * Initial revision
217 *
218 *
219 **********************************************************************/
220
221#include <config.h>
222#include <ctype.h>
223#include "drivermanager.h"
224#if defined ( COLLECT_STATS ) && defined( HAVE_SYS_SEM_H )
225#include "__stats.h"
226#include <uodbc_stats.h>
227#endif
228
229static char const rcsid[]= "$RCSfile: __handles.c,v $ $Revision: 1.13 $";
230
231/*
232 * these are used to enable us to check if a handle is
233 * valid without the danger of a seg-vio.
234 */
235
236static DMHENV environment_root;
237static DMHDBC connection_root;
238static DMHSTMT statement_root;
239static DMHDESC descriptor_root;
240
241
242/*
243 * use just one mutex for all the lists, this avoids any issues
244 * with deadlocks, the performance issue should be minimal, if it
245 * turns out to be a problem, we can readdress this
246 *
247 * We also have a mutex to protect the connection pooling code
248 *
249 * If compiled with thread support the DM allows four different
250 * thread strategies:
251 *
252 * Level 0 - Only the DM internal structures are protected.
253 * The driver is assumed to take care of itself
254 *
255 * Level 1 - The driver is protected down to the statement level.
256 * Each statement will be protected, and the same for the connect
257 * level for connect functions. Note that descriptors are considered
258 * equal to statements when it comes to thread protection.
259 *
260 * Level 2 - The driver is protected at the connection level. Only
261 * one thread can be in a particular driver at one time.
262 *
263 * Level 3 - The driver is protected at the env level, only one thing
264 * at a time.
265 *
266 * By default the driver opens connections with lock level 0; drivers
267 * are expected to be thread safe now. This can be changed by adding
268 * the line
269 *
270 * Threading = N
271 *
272 * to the driver entry in odbcinst.ini, where N is the locking level
273 * (0-3)
274 *
275 */
276
277#ifdef HAVE_LIBPTH
278
279#include <pth.h>
280
281static pth_mutex_t mutex_lists = PTH_MUTEX_INIT;
282static pth_mutex_t mutex_env = PTH_MUTEX_INIT;
283static pth_mutex_t mutex_pool = PTH_MUTEX_INIT;
284static pth_mutex_t mutex_iconv = PTH_MUTEX_INIT;
285static int pth_init_called = 0;
286
287static int local_mutex_entry( pth_mutex_t *mutex )
288{
289 if ( !pth_init_called )
290 {
291 pth_init();
292 pth_init_called = 1;
293 }
294 return pth_mutex_acquire( mutex, 0, NULL );
295}
296
297static int local_mutex_exit( pth_mutex_t *mutex )
298{
299 return pth_mutex_release( mutex );
300}
301
302#elif HAVE_LIBPTHREAD
303
304#include <pthread.h>
305
306static pthread_mutex_t mutex_lists = PTHREAD_MUTEX_INITIALIZER;
307static pthread_mutex_t mutex_env = PTHREAD_MUTEX_INITIALIZER;
308static pthread_mutex_t mutex_pool = PTHREAD_MUTEX_INITIALIZER;
309static pthread_mutex_t mutex_iconv = PTHREAD_MUTEX_INITIALIZER;
310
311static int local_mutex_entry( pthread_mutex_t *mutex )
312{
313 return pthread_mutex_lock( mutex );
314}
315
316static int local_mutex_exit( pthread_mutex_t *mutex )
317{
318 return pthread_mutex_unlock( mutex );
319}
320
321#elif HAVE_LIBTHREAD
322
323#include <thread.h>
324
325static mutex_t mutex_lists;
326static mutex_t mutex_env;
327static mutex_t mutex_pool;
328static mutex_t mutex_iconv;
329
330static int local_mutex_entry( mutex_t *mutex )
331{
332 return mutex_lock( mutex );
333}
334
335static int local_mutex_exit( mutex_t *mutex )
336{
337 return mutex_unlock( mutex );
338}
339
340#else
341
342#define local_mutex_entry(x)
343#define local_mutex_exit(x)
344
345#endif
346
347/*
348 * protection for connection pooling
349 */
350
351void mutex_pool_entry( void )
352{
353 local_mutex_entry( &mutex_pool );
354}
355
356void mutex_pool_exit( void )
357{
358 local_mutex_exit( &mutex_pool );
359}
360
361/*
362 * protection for iconv
363 */
364
365void mutex_iconv_entry( void )
366{
367 local_mutex_entry( &mutex_iconv );
368}
369
370void mutex_iconv_exit( void )
371{
372 local_mutex_exit( &mutex_iconv );
373}
374
375/*
376 * protection for lib loading and counting, reuse the lists mutex as this
377 * is the lowest level protection the DM uses
378 */
379
380void mutex_lib_entry( void )
381{
382 local_mutex_entry( &mutex_lists );
383}
384
385void mutex_lib_exit( void )
386{
387 local_mutex_exit( &mutex_lists );
388}
389
390/*
391 * allocate and register a environment handle
392 */
393
394DMHENV __alloc_env( void )
395{
396 DMHENV environment = NULL;
397
398 local_mutex_entry( &mutex_lists );
399
400 environment = calloc( sizeof( *environment ), 1 );
401
402 if ( environment )
403 {
404 char tracing_string[ 64 ];
405 char tracing_file[ 64 ];
406
407#if defined ( COLLECT_STATS ) && defined( HAVE_SYS_SEM_H )
408 if (uodbc_open_stats(&environment->sh, UODBC_STATS_WRITE) != 0)
409 {
410 ;
411 }
412 uodbc_update_stats(environment->sh, UODBC_STATS_TYPE_HENV, (void *)1);
413#endif
414
415 /*
416 * add to list of env handles
417 */
418
419 environment -> next_class_list = environment_root;
420 environment_root = environment;
421 environment -> type = HENV_MAGIC;
422
423 SQLGetPrivateProfileString( "ODBC", "Trace", "No",
424 tracing_string, sizeof( tracing_string ),
425 "odbcinst.ini" );
426
427 if ( tracing_string[ 0 ] == '1' ||
428 toupper( tracing_string[ 0 ] ) == 'Y' ||
429 ( toupper( tracing_string[ 0 ] ) == 'O' &&
430 toupper( tracing_string[ 1 ] ) == 'N' ))
431 {
432 SQLGetPrivateProfileString( "ODBC", "TraceFile", "/tmp/sql.log",
433 tracing_file, sizeof( tracing_file ),
434 "odbcinst.ini" );
435
436 /*
437 * start logging
438 */
439
440 SQLGetPrivateProfileString( "ODBC", "TracePid", "No",
441 tracing_string, sizeof( tracing_string ),
442 "odbcinst.ini" );
443
444 if ( tracing_string[ 0 ] == '1' ||
445 toupper( tracing_string[ 0 ] ) == 'Y' ||
446 ( toupper( tracing_string[ 0 ] ) == 'O' &&
447 toupper( tracing_string[ 1 ] ) == 'N' ))
448 {
449 dm_log_open( "ODBC", tracing_file, 1 );
450 }
451 else
452 {
453 dm_log_open( "ODBC", tracing_file, 0 );
454 }
455
456 sprintf( environment -> msg,
457 "\n\t\tExit:[SQL_SUCCESS]\n\t\t\tEnvironment = %p", environment );
458
459 dm_log_write( __FILE__,
460 __LINE__,
461 LOG_INFO,
462 LOG_INFO, environment -> msg );
463 }
464 setup_error_head( &environment -> error, environment,
465 SQL_HANDLE_ENV );
466
467 }
468
469 local_mutex_exit( &mutex_lists );
470
471 return environment;
472}
473
474/*
475 * check that a env is real
476 */
477
478int __validate_env( DMHENV env )
479{
480#ifdef FAST_HANDLE_VALIDATE
481
482 if ( env && *(( int * ) env ) == HENV_MAGIC )
483 return 1;
484 else
485 return 0;
486
487#else
488
489 DMHENV ptr;
490 int ret = 0;
491
492 local_mutex_entry( &mutex_lists );
493
494 ptr = environment_root;
495
496 while( ptr )
497 {
498 if ( ptr == env )
499 {
500 ret = 1;
501 break;
502 }
503
504 ptr = ptr -> next_class_list;
505 }
506
507 local_mutex_exit( &mutex_lists );
508
509 return ret;
510
511#endif
512}
513
514/*
515 * remove from list
516 */
517
518void __release_env( DMHENV environment )
519{
520 DMHENV last = NULL;
521 DMHENV ptr;
522
523 local_mutex_entry( &mutex_lists );
524
525 ptr = environment_root;
526
527 while( ptr )
528 {
529 if ( environment == ptr )
530 {
531 break;
532 }
533 last = ptr;
534 ptr = ptr -> next_class_list;
535 }
536
537 if ( ptr )
538 {
539 if ( last )
540 {
541 last -> next_class_list = ptr -> next_class_list;
542 }
543 else
544 {
545 environment_root = ptr -> next_class_list;
546 }
547 }
548
549 clear_error_head( &environment -> error );
550
551 /*
552 * free log
553 */
554
555 dm_log_close();
556
557#if defined ( COLLECT_STATS ) && defined( HAVE_SYS_SEM_H )
558 if (environment->sh)
559 uodbc_close_stats(environment->sh);
560#endif
561
562 /*
563 * clear just to make sure
564 */
565
566 memset( environment, 0, sizeof( *environment ));
567
568 free( environment );
569
570 local_mutex_exit( &mutex_lists );
571}
572
573/*
574 * get the root, for use in SQLEndTran and SQLTransact
575 */
576
577DMHDBC __get_dbc_root( void )
578{
579 return connection_root;
580}
581
582/*
583 * allocate and register a connection handle
584 */
585
586DMHDBC __alloc_dbc( void )
587{
588 DMHDBC connection = NULL;
589
590 local_mutex_entry( &mutex_lists );
591
592 connection = calloc( sizeof( *connection ), 1 );
593
594 if ( connection )
595 {
596 /*
597 * add to list of connection handles
598 */
599
600 connection -> next_class_list = connection_root;
601 connection_root = connection;
602 connection -> type = HDBC_MAGIC;
603
604 setup_error_head( &connection -> error, connection,
605 SQL_HANDLE_DBC );
606
607#ifdef HAVE_LIBPTH
608 pth_mutex_init( &connection -> mutex );
609 /*
610 * for the moment protect at the environment level
611 */
612 connection -> protection_level = TS_LEVEL3;
613#elif HAVE_LIBPTHREAD
614 pthread_mutex_init( &connection -> mutex, NULL );
615 /*
616 * for the moment protect at the environment level
617 */
618 connection -> protection_level = TS_LEVEL3;
619#elif HAVE_LIBTHREAD
620 mutex_init( &connection -> mutex, USYNC_THREAD, NULL );
621 connection -> protection_level = TS_LEVEL3;
622#endif
623
624#ifdef HAVE_ICONV
625 connection -> iconv_cd_uc_to_ascii = (iconv_t)(-1);
626 connection -> iconv_cd_ascii_to_uc = (iconv_t)(-1);
627#endif
628 }
629
630 local_mutex_exit( &mutex_lists );
631
632 return connection;
633}
634
635/*
636 * adjust the threading level
637 */
638
639void dbc_change_thread_support( DMHDBC connection, int level )
640{
641#if defined ( HAVE_LIBPTHREAD ) || defined( HAVE_LIBTHREAD ) || defined( HAVE_LIBPTH )
642 int old_level;
643
644 if ( connection -> protection_level == level )
645 return;
646
647 old_level = connection -> protection_level;
648 connection -> protection_level = level;
649
650 if ( level == TS_LEVEL3 )
651 {
652 /*
653 * if we are moving from level 3 we may have to release the existing
654 * connection lock, and create the env lock
655 */
656 if(old_level != TS_LEVEL0)
657 local_mutex_exit( &connection -> mutex );
658 local_mutex_entry( &mutex_env );
659 }
660 else if ( old_level == TS_LEVEL3 )
661 {
662 /*
663 * if we are moving from level 3 we may have to create the new
664 * connection lock, and remove the env lock
665 */
666 if(level != TS_LEVEL0)
667 local_mutex_entry( &connection -> mutex );
668 local_mutex_exit( &mutex_env );
669 }
670
671#endif
672}
673
674/*
675 * check that a connection is real
676 */
677
678int __validate_dbc( DMHDBC connection )
679{
680#ifdef FAST_HANDLE_VALIDATE
681
682 if ( connection && *(( int * ) connection ) == HDBC_MAGIC )
683 return 1;
684 else
685 return 0;
686
687#else
688
689 DMHDBC ptr;
690 int ret = 0;
691
692 local_mutex_entry( &mutex_lists );
693
694 ptr = connection_root;
695
696 while( ptr )
697 {
698 if ( ptr == connection )
699 {
700 ret = 1;
701 break;
702 }
703
704 ptr = ptr -> next_class_list;
705 }
706
707 local_mutex_exit( &mutex_lists );
708
709 return ret;
710#endif
711}
712
713/*
714 * remove from list
715 */
716
717void __release_dbc( DMHDBC connection )
718{
719 DMHDBC last = NULL;
720 DMHDBC ptr;
721
722 local_mutex_entry( &mutex_lists );
723
724 ptr = connection_root;
725
726 while( ptr )
727 {
728 if ( connection == ptr )
729 {
730 break;
731 }
732 last = ptr;
733 ptr = ptr -> next_class_list;
734 }
735
736 if ( ptr )
737 {
738 if ( last )
739 {
740 last -> next_class_list = ptr -> next_class_list;
741 }
742 else
743 {
744 connection_root = ptr -> next_class_list;
745 }
746 }
747
748 clear_error_head( &connection -> error );
749
750 /*
751 * shutdown unicode
752 */
753
754 unicode_shutdown( connection );
755
756#ifdef HAVE_LIBPTH
757#elif HAVE_LIBPTHREAD
758 pthread_mutex_destroy( &connection -> mutex );
759#elif HAVE_LIBTHREAD
760 mutex_destroy( &connection -> mutex );
761#endif
762
763 /*
764 * clear just to make sure
765 */
766
767 memset( connection, 0, sizeof( *connection ));
768
769 free( connection );
770
771 local_mutex_exit( &mutex_lists );
772}
773
774/*
775 * allocate and register a statement handle
776 */
777
778DMHSTMT __alloc_stmt( void )
779{
780 DMHSTMT statement = NULL;
781
782 local_mutex_entry( &mutex_lists );
783
784 statement = calloc( sizeof( *statement ), 1 );
785
786 if ( statement )
787 {
788 /*
789 * add to list of statement handles
790 */
791
792 statement -> next_class_list = statement_root;
793#ifdef FAST_HANDLE_VALIDATE
794 if ( statement_root )
795 {
796 statement_root -> prev_class_list = statement;
797 }
798#endif
799 statement_root = statement;
800 statement -> type = HSTMT_MAGIC;
801
802 setup_error_head( &statement -> error, statement,
803 SQL_HANDLE_STMT );
804
805#ifdef HAVE_LIBPTH
806 pth_mutex_init( &statement -> mutex );
807#elif HAVE_LIBPTHREAD
808 pthread_mutex_init( &statement -> mutex, NULL );
809#elif HAVE_LIBTHREAD
810 mutex_init( &statement -> mutex, USYNC_THREAD, NULL );
811#endif
812
813 }
814
815 local_mutex_exit( &mutex_lists );
816
817 return statement;
818}
819
820/*
821 * assigns a statements to the connection
822 */
823
824void __register_stmt ( DMHDBC connection, DMHSTMT statement )
825{
826 local_mutex_entry( &mutex_lists );
827
828 connection -> statement_count ++;
829 statement -> connection = connection;
830#ifdef FAST_HANDLE_VALIDATE
831 statement -> next_conn_list = connection -> statements;
832 connection -> statements = statement;
833#endif
834 local_mutex_exit( &mutex_lists );
835}
836
837/*
838 * Sets statement state after commit or rollback transaction
839 */
840void __set_stmt_state ( DMHDBC connection, SQLSMALLINT cb_value )
841{
842 DMHSTMT statement;
843 SQLINTEGER stmt_remaining;
844
845 local_mutex_entry( &mutex_lists );
846#ifdef FAST_HANDLE_VALIDATE
847 statement = connection -> statements;
848 while ( statement )
849 {
850 if ( (statement -> state == STATE_S2 ||
851 statement -> state == STATE_S3) &&
852 cb_value == SQL_CB_DELETE )
853 {
854 statement -> state = STATE_S1;
855 statement -> prepared = 0;
856 }
857 else if ( statement -> state == STATE_S4 ||
858 statement -> state == STATE_S5 ||
859 statement -> state == STATE_S6 ||
860 statement -> state == STATE_S7 )
861 {
862 if( !statement -> prepared &&
863 (cb_value == SQL_CB_DELETE ||
864 cb_value == SQL_CB_CLOSE) )
865 {
866 statement -> state = STATE_S1;
867 }
868 else if( statement -> prepared )
869 {
870 if( cb_value == SQL_CB_DELETE )
871 {
872 statement -> state = STATE_S1;
873 statement -> prepared = 0;
874 }
875 else if( cb_value == SQL_CB_CLOSE )
876 {
877 if ( statement -> state == STATE_S4 )
878 statement -> state = STATE_S2;
879 else
880 statement -> state = STATE_S3;
881 }
882 }
883 }
884 statement = statement -> next_conn_list;
885 }
886#else
887 statement = statement_root;
888 stmt_remaining = connection -> statement_count;
889
890 while ( statement && stmt_remaining > 0 )
891 {
892 if ( statement -> connection == connection )
893 {
894 if ( (statement -> state == STATE_S2 ||
895 statement -> state == STATE_S3) &&
896 cb_value == SQL_CB_DELETE )
897 {
898 statement -> state = STATE_S1;
899 statement -> prepared = 0;
900 }
901 else if ( statement -> state == STATE_S4 ||
902 statement -> state == STATE_S5 ||
903 statement -> state == STATE_S6 ||
904 statement -> state == STATE_S7 )
905 {
906 if( !statement -> prepared &&
907 (cb_value == SQL_CB_DELETE ||
908 cb_value == SQL_CB_CLOSE) )
909 {
910 statement -> state = STATE_S1;
911 }
912 else if( statement -> prepared )
913 {
914 if( cb_value == SQL_CB_DELETE )
915 {
916 statement -> state = STATE_S1;
917 statement -> prepared = 0;
918 }
919 else if( cb_value == SQL_CB_CLOSE )
920 {
921 if ( statement -> state == STATE_S4 )
922 statement -> state = STATE_S2;
923 else
924 statement -> state = STATE_S3;
925 }
926 }
927 }
928
929 stmt_remaining --;
930 }
931
932 statement = statement -> next_class_list;
933 }
934#endif
935 local_mutex_exit( &mutex_lists );
936}
937
938/*
939 * clear all statements on a DBC
940 */
941
942int __clean_stmt_from_dbc( DMHDBC connection )
943{
944 DMHSTMT ptr, last;
945 int ret = 0;
946
947 local_mutex_entry( &mutex_lists );
948#ifdef FAST_HANDLE_VALIDATE
949 while ( connection -> statements )
950 {
951 ptr = connection -> statements;
952 last = connection -> statements -> prev_class_list;
953
954 connection -> statements = ptr -> next_conn_list;
955 if ( last )
956 {
957 last -> next_class_list = ptr -> next_class_list;
958 if ( last -> next_class_list )
959 {
960 last -> next_class_list -> prev_class_list = last;
961 }
962 }
963 else
964 {
965 statement_root = ptr -> next_class_list;
966 if ( statement_root )
967 {
968 statement_root -> prev_class_list = NULL;
969 }
970 }
971 clear_error_head( &ptr -> error );
972
973#ifdef HAVE_LIBPTH
974#elif HAVE_LIBPTHREAD
975 pthread_mutex_destroy( &ptr -> mutex );
976#elif HAVE_LIBTHREAD
977 mutex_destroy( &ptr -> mutex );
978#endif
979 free( ptr );
980 }
981#else
982 last = NULL;
983 ptr = statement_root;
984
985 while( ptr )
986 {
987 if ( ptr -> connection == connection )
988 {
989 if ( last )
990 {
991 last -> next_class_list = ptr -> next_class_list;
992 }
993 else
994 {
995 statement_root = ptr -> next_class_list;
996 }
997 clear_error_head( &ptr -> error );
998
999#ifdef HAVE_LIBPTH
1000#elif HAVE_LIBPTHREAD
1001 pthread_mutex_destroy( &ptr -> mutex );
1002#elif HAVE_LIBTHREAD
1003 mutex_destroy( &ptr -> mutex );
1004#endif
1005 free( ptr );
1006
1007 /*
1008 * go back to the start
1009 */
1010
1011 last = NULL;
1012 ptr = statement_root;
1013 }
1014 else
1015 {
1016 last = ptr;
1017 ptr = ptr -> next_class_list;
1018 }
1019 }
1020#endif
1021 local_mutex_exit( &mutex_lists );
1022
1023 return ret;
1024}
1025
1026int __check_stmt_from_dbc_v( DMHDBC connection, int statecount, ... )
1027{
1028 va_list ap;
1029 int states[ MAX_STATE_ARGS ];
1030 DMHSTMT ptr;
1031 int found = 0;
1032 int i;
1033
1034 va_start (ap, statecount);
1035 for ( i = 0; i < statecount; i ++ ) {
1036 states[ i ] = va_arg (ap, int );
1037 }
1038 va_end (ap);
1039
1040 local_mutex_entry( &mutex_lists );
1041#ifdef FAST_HANDLE_VALIDATE
1042 ptr = connection -> statements;
1043 while( !found && ptr )
1044 {
1045 for ( i = 0; i < statecount; i ++ ) {
1046 if ( ptr -> state == states[ i ] ) {
1047 found = 1;
1048 break;
1049 }
1050 }
1051
1052 ptr = ptr -> next_conn_list;
1053 }
1054#else
1055 ptr = statement_root;
1056 while( !found && ptr )
1057 {
1058 if ( ptr -> connection == connection )
1059 {
1060 for ( i = 0; i < statecount; i ++ ) {
1061 if ( ptr -> state == states[ i ] ) {
1062 found = 1;
1063 break;
1064 }
1065 }
1066 }
1067
1068 ptr = ptr -> next_class_list;
1069 }
1070#endif
1071 local_mutex_exit( &mutex_lists );
1072
1073 return found;
1074}
1075
1076/*
1077 * check if any statements on this connection are in a given state
1078 */
1079
1080int __check_stmt_from_dbc( DMHDBC connection, int state )
1081{
1082 DMHSTMT ptr;
1083 int found = 0;
1084
1085 local_mutex_entry( &mutex_lists );
1086#ifdef FAST_HANDLE_VALIDATE
1087 ptr = connection -> statements;
1088 while( ptr )
1089 {
1090 if ( ptr -> state == state )
1091 {
1092 found = 1;
1093 break;
1094 }
1095
1096 ptr = ptr -> next_conn_list;
1097 }
1098#else
1099 ptr = statement_root;
1100 while( ptr )
1101 {
1102 if ( ptr -> connection == connection )
1103 {
1104 if ( ptr -> state == state )
1105 {
1106 found = 1;
1107 break;
1108 }
1109 }
1110
1111 ptr = ptr -> next_class_list;
1112 }
1113#endif
1114 local_mutex_exit( &mutex_lists );
1115
1116 return found;
1117}
1118
1119int __check_stmt_from_desc( DMHDESC desc, int state )
1120{
1121 DMHDBC connection;
1122 DMHSTMT ptr;
1123 int found = 0;
1124
1125 local_mutex_entry( &mutex_lists );
1126 connection = desc -> connection;
1127#ifdef FAST_HANDLE_VALIDATE
1128 ptr = connection -> statements;
1129 while( ptr )
1130 {
1131 if ( ptr -> ipd == desc || ptr -> ird == desc || ptr -> apd == desc || ptr -> ard == desc )
1132 {
1133 if ( ptr -> state == state )
1134 {
1135 found = 1;
1136 break;
1137 }
1138 }
1139
1140 ptr = ptr -> next_conn_list;
1141 }
1142#else
1143 ptr = statement_root;
1144 while( ptr )
1145 {
1146 if ( ptr -> connection == connection )
1147 {
1148 if ( ptr -> ipd == desc || ptr -> ird == desc || ptr -> apd == desc || ptr -> ard == desc )
1149 {
1150 if ( ptr -> state == state )
1151 {
1152 found = 1;
1153 break;
1154 }
1155 }
1156 }
1157
1158 ptr = ptr -> next_class_list;
1159 }
1160#endif
1161 local_mutex_exit( &mutex_lists );
1162
1163 return found;
1164}
1165
1166int __check_stmt_from_desc_ird( DMHDESC desc, int state )
1167{
1168 DMHDBC connection;
1169 DMHSTMT ptr;
1170 int found = 0;
1171
1172 local_mutex_entry( &mutex_lists );
1173 connection = desc -> connection;
1174#ifdef FAST_HANDLE_VALIDATE
1175 ptr = connection -> statements;
1176 while( ptr )
1177 {
1178 if ( ptr -> ird == desc )
1179 {
1180 if ( ptr -> state == state )
1181 {
1182 found = 1;
1183 break;
1184 }
1185 }
1186
1187 ptr = ptr -> next_conn_list;
1188 }
1189#else
1190 ptr = statement_root;
1191 while( ptr )
1192 {
1193 if ( ptr -> connection == connection )
1194 {
1195 if ( ptr -> ird == desc )
1196 {
1197 if ( ptr -> state == state )
1198 {
1199 found = 1;
1200 break;
1201 }
1202 }
1203 }
1204
1205 ptr = ptr -> next_class_list;
1206 }
1207#endif
1208 local_mutex_exit( &mutex_lists );
1209
1210 return found;
1211}
1212
1213/*
1214 * check any statements that are associated with a descriptor
1215 */
1216
1217/*
1218 * check that a statement is real
1219 */
1220
1221int __validate_stmt( DMHSTMT statement )
1222{
1223#ifdef FAST_HANDLE_VALIDATE
1224
1225 if ( statement && *(( int * ) statement ) == HSTMT_MAGIC )
1226 return 1;
1227 else
1228 return 0;
1229
1230#else
1231
1232 DMHSTMT ptr;
1233 int ret = 0;
1234
1235 local_mutex_entry( &mutex_lists );
1236
1237 ptr = statement_root;
1238
1239 while( ptr )
1240 {
1241 if ( ptr == statement )
1242 {
1243 ret = 1;
1244 break;
1245 }
1246
1247 ptr = ptr -> next_class_list;
1248 }
1249
1250 local_mutex_exit( &mutex_lists );
1251
1252 return ret;
1253
1254#endif
1255}
1256
1257/*
1258 * remove from list
1259 */
1260
1261void __release_stmt( DMHSTMT statement )
1262{
1263 DMHSTMT last = NULL;
1264 DMHSTMT ptr;
1265
1266 local_mutex_entry( &mutex_lists );
1267#ifdef FAST_HANDLE_VALIDATE
1268 /*
1269 * A check never mind
1270 */
1271 if ( statement && ( *(( int * ) statement ) == HSTMT_MAGIC ))
1272 {
1273 ptr = statement;
1274 last = statement->prev_class_list;
1275
1276 if ( statement -> connection )
1277 {
1278 DMHDBC connection = statement -> connection;
1279 DMHSTMT conn_last = NULL;
1280 DMHSTMT conn_ptr = connection -> statements;
1281 while ( conn_ptr )
1282 {
1283 if ( statement == conn_ptr )
1284 {
1285 break;
1286 }
1287 conn_last = conn_ptr;
1288 conn_ptr = conn_ptr -> next_conn_list;
1289 }
1290 if ( conn_ptr )
1291 {
1292 if ( conn_last )
1293 {
1294 conn_last -> next_conn_list = conn_ptr -> next_conn_list;
1295 }
1296 else
1297 {
1298 connection -> statements = conn_ptr -> next_conn_list;
1299 }
1300 }
1301 }
1302 }
1303 else
1304 {
1305 ptr = NULL;
1306 last = NULL;
1307 }
1308#else
1309 ptr = statement_root;
1310
1311 while( ptr )
1312 {
1313 if ( statement == ptr )
1314 {
1315 break;
1316 }
1317 last = ptr;
1318 ptr = ptr -> next_class_list;
1319 }
1320#endif
1321 if ( ptr )
1322 {
1323 if ( last )
1324 {
1325 last -> next_class_list = ptr -> next_class_list;
1326#ifdef FAST_HANDLE_VALIDATE
1327 if ( last -> next_class_list )
1328 {
1329 last -> next_class_list -> prev_class_list = last;
1330 }
1331#endif
1332 }
1333 else
1334 {
1335 statement_root = ptr -> next_class_list;
1336#ifdef FAST_HANDLE_VALIDATE
1337 if ( statement_root )
1338 {
1339 statement_root -> prev_class_list = NULL;
1340 }
1341#endif
1342 }
1343 }
1344
1345 clear_error_head( &statement -> error );
1346
1347#ifdef HAVE_LIBPTH
1348#elif HAVE_LIBPTHREAD
1349 pthread_mutex_destroy( &statement -> mutex );
1350#elif HAVE_LIBTHREAD
1351 mutex_destroy( &statement -> mutex );
1352#endif
1353
1354 /*
1355 * clear just to make sure
1356 */
1357
1358 memset( statement, 0, sizeof( *statement ));
1359
1360 free( statement );
1361
1362 local_mutex_exit( &mutex_lists );
1363}
1364
1365/*
1366 * allocate and register a descriptor handle
1367 */
1368
1369DMHDESC __alloc_desc( void )
1370{
1371 DMHDESC descriptor;
1372
1373 local_mutex_entry( &mutex_lists );
1374
1375 descriptor = calloc( sizeof( *descriptor ), 1 );
1376
1377 if ( descriptor )
1378 {
1379 /*
1380 * add to list of descriptor handles
1381 */
1382
1383 descriptor -> next_class_list = descriptor_root;
1384#ifdef FAST_HANDLE_VALIDATE
1385 if ( descriptor_root )
1386 {
1387 descriptor_root -> prev_class_list = descriptor;
1388 }
1389#endif
1390 descriptor_root = descriptor;
1391 descriptor -> type = HDESC_MAGIC;
1392
1393 setup_error_head( &descriptor -> error, descriptor, SQL_HANDLE_DESC );
1394
1395#ifdef HAVE_LIBPTH
1396 pth_mutex_init( &descriptor -> mutex );
1397#elif HAVE_LIBPTHREAD
1398 pthread_mutex_init( &descriptor -> mutex, NULL );
1399#elif HAVE_LIBTHREAD
1400 mutex_init( &descriptor -> mutex, USYNC_THREAD, NULL );
1401#endif
1402 }
1403
1404 local_mutex_exit( &mutex_lists );
1405
1406 return descriptor;
1407}
1408
1409/*
1410 * check that a descriptor is real
1411 */
1412
1413int __validate_desc( DMHDESC descriptor )
1414{
1415#ifdef FAST_HANDLE_VALIDATE
1416
1417 if ( descriptor && *(( int * ) descriptor ) == HDESC_MAGIC )
1418 return 1;
1419 else
1420 return 0;
1421
1422#else
1423
1424 DMHDESC ptr;
1425 int ret = 0;
1426
1427 local_mutex_entry( &mutex_lists );
1428
1429 ptr = descriptor_root;
1430
1431 while( ptr )
1432 {
1433 if ( ptr == descriptor )
1434 {
1435 ret = 1;
1436 break;
1437 }
1438
1439 ptr = ptr -> next_class_list;
1440 }
1441
1442 local_mutex_exit( &mutex_lists );
1443
1444 return ret;
1445
1446#endif
1447}
1448
1449/*
1450 * clear all descriptors on a DBC
1451 */
1452
1453int __clean_desc_from_dbc( DMHDBC connection )
1454{
1455 DMHDESC ptr, last;
1456 int ret = 0;
1457
1458 local_mutex_entry( &mutex_lists );
1459 last = NULL;
1460 ptr = descriptor_root;
1461
1462 while( ptr )
1463 {
1464 if ( ptr -> connection == connection )
1465 {
1466 if ( last )
1467 {
1468 last -> next_class_list = ptr -> next_class_list;
1469#ifdef FAST_HANDLE_VALIDATE
1470 if ( last -> next_class_list )
1471 {
1472 last -> next_class_list -> prev_class_list = last;
1473 }
1474#endif
1475 }
1476 else
1477 {
1478 descriptor_root = ptr -> next_class_list;
1479#ifdef FAST_HANDLE_VALIDATE
1480 if ( descriptor_root )
1481 {
1482 descriptor_root -> prev_class_list = NULL;
1483 }
1484#endif
1485 }
1486 clear_error_head( &ptr -> error );
1487
1488#ifdef HAVE_LIBPTH
1489#elif HAVE_LIBPTHREAD
1490 pthread_mutex_destroy( &ptr -> mutex );
1491#elif HAVE_LIBTHREAD
1492 mutex_destroy( &ptr -> mutex );
1493#endif
1494 free( ptr );
1495
1496 /*
1497 * go back to the start
1498 */
1499
1500 last = NULL;
1501 ptr = descriptor_root;
1502 }
1503 else
1504 {
1505 last = ptr;
1506 ptr = ptr -> next_class_list;
1507 }
1508 }
1509
1510 local_mutex_exit( &mutex_lists );
1511
1512 return ret;
1513}
1514
1515
1516/*
1517 * remove from list
1518 */
1519
1520void __release_desc( DMHDESC descriptor )
1521{
1522 DMHDESC last = NULL;
1523 DMHDESC ptr;
1524 DMHSTMT assoc_stmt;
1525
1526 local_mutex_entry( &mutex_lists );
1527#ifdef FAST_HANDLE_VALIDATE
1528 /*
1529 * A check never mind
1530 */
1531 if ( descriptor && ( *(( int * ) descriptor ) == HDESC_MAGIC ))
1532 {
1533 ptr = descriptor;
1534 last = descriptor->prev_class_list;
1535 }
1536 else
1537 {
1538 ptr = NULL;
1539 last = NULL;
1540 }
1541#else
1542 ptr = descriptor_root;
1543
1544 while( ptr )
1545 {
1546 if ( descriptor == ptr )
1547 {
1548 break;
1549 }
1550 last = ptr;
1551 ptr = ptr -> next_class_list;
1552 }
1553#endif
1554
1555 if ( ptr )
1556 {
1557 if ( last )
1558 {
1559 last -> next_class_list = ptr -> next_class_list;
1560#ifdef FAST_HANDLE_VALIDATE
1561 if ( last -> next_class_list )
1562 {
1563 last -> next_class_list -> prev_class_list = last;
1564 }
1565#endif
1566 }
1567 else
1568 {
1569 descriptor_root = ptr -> next_class_list;
1570#ifdef FAST_HANDLE_VALIDATE
1571 if ( descriptor_root )
1572 {
1573 descriptor_root -> prev_class_list = NULL;
1574 }
1575#endif
1576 }
1577 }
1578
1579 clear_error_head( &descriptor -> error );
1580 /* If there are any statements still pointing to this descriptor, revert them to implicit */
1581 assoc_stmt = statement_root;
1582 while ( assoc_stmt )
1583 {
1584 DMHDESC *pDesc[] = {
1585 &assoc_stmt -> ipd, &assoc_stmt -> apd, &assoc_stmt -> ird, &assoc_stmt -> ard
1586 };
1587 DMHDESC impDesc[] = {
1588 assoc_stmt -> implicit_ipd, assoc_stmt -> implicit_apd,
1589 assoc_stmt -> implicit_ird, assoc_stmt -> implicit_ard
1590 };
1591 int i;
1592 for ( i = 0; i < 4; i++ )
1593 {
1594 if ( *pDesc[i] == descriptor )
1595 {
1596 *pDesc[i] = impDesc[i];
1597 }
1598 }
1599 assoc_stmt = assoc_stmt -> next_class_list;
1600 }
1601
1602#ifdef HAVE_LIBPTH
1603#elif HAVE_LIBPTHREAD
1604 pthread_mutex_destroy( &descriptor -> mutex );
1605#elif HAVE_LIBTHREAD
1606 mutex_destroy( &descriptor -> mutex );
1607#endif
1608
1609 /*
1610 * clear just to make sure
1611 */
1612
1613 memset( descriptor, 0, sizeof( *descriptor ));
1614
1615 free( descriptor );
1616
1617 local_mutex_exit( &mutex_lists );
1618}
1619
1620#if defined ( HAVE_LIBPTHREAD ) || defined ( HAVE_LIBTHREAD ) || defined( HAVE_LIBPTH )
1621
1622void thread_protect( int type, void *handle )
1623{
1624 DMHDBC connection;
1625 DMHSTMT statement;
1626 DMHDESC descriptor;
1627
1628 switch( type )
1629 {
1630 case SQL_HANDLE_ENV:
1631 local_mutex_entry( &mutex_env );
1632 break;
1633
1634 case SQL_HANDLE_DBC:
1635 connection = handle;
1636 if ( connection -> protection_level == TS_LEVEL3 )
1637 {
1638 local_mutex_entry( &mutex_env );
1639 }
1640 else if ( connection -> protection_level == TS_LEVEL2 ||
1641 connection -> protection_level == TS_LEVEL1 )
1642 {
1643 local_mutex_entry( &connection -> mutex );
1644 }
1645 break;
1646
1647 case SQL_HANDLE_STMT:
1648 statement = handle;
1649 if ( statement -> connection -> protection_level == TS_LEVEL3 )
1650 {
1651 local_mutex_entry( &mutex_env );
1652 }
1653 else if ( statement -> connection -> protection_level == TS_LEVEL2 )
1654 {
1655 local_mutex_entry( &statement -> connection -> mutex );
1656 }
1657 else if ( statement -> connection -> protection_level == TS_LEVEL1 )
1658 {
1659 local_mutex_entry( &statement -> mutex );
1660 }
1661 break;
1662
1663 case SQL_HANDLE_DESC:
1664 descriptor = handle;
1665 if ( descriptor -> connection -> protection_level == TS_LEVEL3 )
1666 {
1667 local_mutex_entry( &mutex_env );
1668 }
1669 if ( descriptor -> connection -> protection_level == TS_LEVEL2 )
1670 {
1671 local_mutex_entry( &descriptor -> connection -> mutex );
1672 }
1673 if ( descriptor -> connection -> protection_level == TS_LEVEL1 )
1674 {
1675 local_mutex_entry( &descriptor -> mutex );
1676 }
1677 break;
1678 }
1679}
1680
1681void thread_release( int type, void *handle )
1682{
1683 DMHDBC connection;
1684 DMHSTMT statement;
1685 DMHDESC descriptor;
1686
1687 switch( type )
1688 {
1689 case SQL_HANDLE_ENV:
1690 local_mutex_exit( &mutex_env );
1691 break;
1692
1693 case SQL_HANDLE_DBC:
1694 connection = handle;
1695 if ( connection -> protection_level == TS_LEVEL3 )
1696 {
1697 local_mutex_exit( &mutex_env );
1698 }
1699 else if ( connection -> protection_level == TS_LEVEL2 ||
1700 connection -> protection_level == TS_LEVEL1 )
1701 {
1702 local_mutex_exit( &connection -> mutex );
1703 }
1704 break;
1705
1706 case SQL_HANDLE_STMT:
1707 statement = handle;
1708 if ( statement -> connection -> protection_level == TS_LEVEL3 )
1709 {
1710 local_mutex_exit( &mutex_env );
1711 }
1712 else if ( statement -> connection -> protection_level == TS_LEVEL2 )
1713 {
1714 local_mutex_exit( &statement -> connection -> mutex );
1715 }
1716 else if ( statement -> connection -> protection_level == TS_LEVEL1 )
1717 {
1718 local_mutex_exit( &statement -> mutex );
1719 }
1720 break;
1721
1722 case SQL_HANDLE_DESC:
1723 descriptor = handle;
1724 if ( descriptor -> connection -> protection_level == TS_LEVEL3 )
1725 {
1726 local_mutex_exit( &mutex_env );
1727 }
1728 else if ( descriptor -> connection -> protection_level == TS_LEVEL2 )
1729 {
1730 local_mutex_exit( &descriptor -> connection -> mutex );
1731 }
1732 else if ( descriptor -> connection -> protection_level == TS_LEVEL1 )
1733 {
1734 local_mutex_exit( &descriptor -> mutex );
1735 }
1736 break;
1737 }
1738}
1739
1740#endif
1741
1742#ifdef WITH_HANDLE_REDIRECT
1743
1744/*
1745 * try and find a handle that has the suplied handle as the driver handle
1746 * there will be threading issues with this, so be carefull.
1747 * However it will normally only get used with "broken" drivers.
1748 */
1749
1750
1751void *find_parent_handle( DRV_SQLHANDLE drv_hand, int type )
1752{
1753 void *found_handle = NULL;
1754
1755 local_mutex_entry( &mutex_lists );
1756
1757 switch( type ) {
1758 case SQL_HANDLE_DBC:
1759 {
1760 DMHDBC hand = connection_root;
1761 while( hand ) {
1762 if ( hand -> driver_dbc == drv_hand ) {
1763 found_handle = hand;
1764 break;
1765 }
1766 hand = hand -> next_class_list;
1767 }
1768 }
1769 break;
1770
1771 case SQL_HANDLE_STMT:
1772 {
1773 DMHSTMT hand = statement_root;
1774 while( hand ) {
1775 if ( hand -> driver_stmt == drv_hand ) {
1776 found_handle = hand;
1777 break;
1778 }
1779 hand = hand -> next_class_list;
1780 }
1781 }
1782 break;
1783
1784 case SQL_HANDLE_DESC:
1785 {
1786 DMHDESC hand = descriptor_root;
1787 while( hand ) {
1788 if ( hand -> driver_desc == drv_hand ) {
1789 found_handle = hand;
1790 break;
1791 }
1792 hand = hand -> next_class_list;
1793 }
1794 }
1795 break;
1796
1797 default:
1798 break;
1799 }
1800
1801 local_mutex_exit( &mutex_lists );
1802
1803 return found_handle;
1804}
1805
1806#endif
1807