SQLite

Check-in [ef99eb57c5]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Trying to open a transaction in one thread and close it in another is a misuse with LinuxThreads. Doing so may cause memory and file-descriptors to be leaked. Update an assert() and some test cases to account for this.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ef99eb57c536d82e7c19fd3d990c17793cc64a3f
User & Date: dan 2009-09-09 18:46:53.000
Context
2009-09-10
02:54
Reduce default SQLITE_MAX_TRIGGER_DEPTH when SQLITE_SMALL_STACK is defined. (check-in: 913fb70ea8 user: shane tags: trunk)
2009-09-09
18:46
Trying to open a transaction in one thread and close it in another is a misuse with LinuxThreads. Doing so may cause memory and file-descriptors to be leaked. Update an assert() and some test cases to account for this. (check-in: ef99eb57c5 user: dan tags: trunk)
16:10
Suppress some harmless compiler warnings. (check-in: f0c72a53c5 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/os_unix.c.
921
922
923
924
925
926
927
928








929
930
931
932
933
934
935
        assert( openList==pOpen );
        openList = pOpen->pNext;
      }
      if( pOpen->pNext ){
        assert( pOpen->pNext->pPrev==pOpen );
        pOpen->pNext->pPrev = pOpen->pPrev;
      }
      assert( !pOpen->pUnused );








      sqlite3_free(pOpen);
    }
  }
}

/*
** Given a file descriptor, locate unixLockInfo and unixOpenCnt structures that







|
>
>
>
>
>
>
>
>







921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
        assert( openList==pOpen );
        openList = pOpen->pNext;
      }
      if( pOpen->pNext ){
        assert( pOpen->pNext->pPrev==pOpen );
        pOpen->pNext->pPrev = pOpen->pPrev;
      }
      assert( !pOpen->pUnused || threadsOverrideEachOthersLocks==0 );

      /* If pOpen->pUnused is not null, then memory and file-descriptors
      ** are leaked.
      **
      ** This will only happen if, under Linuxthreads, the user has opened
      ** a transaction in one thread, then attempts to close the database
      ** handle from another thread (without first unlocking the db file).
      ** This is a misuse.  */
      sqlite3_free(pOpen);
    }
  }
}

/*
** Given a file descriptor, locate unixLockInfo and unixOpenCnt structures that
Changes to src/test4.c.
645
646
647
648
649
650
651






























652
653
654
655
656
657
658
  }
  thread_wait(&threadset[i]);
  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
  threadset[i].db = 0;
  Tcl_AppendResult(interp, zBuf, (char*)0);
  return TCL_OK;
}































/*
** Usage: thread_stmt_get ID
**
** Return the database stmt pointer for the given thread.  Then
** remove the pointer from the thread itself. 
*/







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
  }
  thread_wait(&threadset[i]);
  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
  threadset[i].db = 0;
  Tcl_AppendResult(interp, zBuf, (char*)0);
  return TCL_OK;
}

/*
** Usage: thread_db_put ID DB
**
*/
static int tcl_thread_db_put(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  const char **argv      /* Text of each argument */
){
  int i;
  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
  extern void *sqlite3TestTextToPtr(const char *);
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID DB", 0);
    return TCL_ERROR;
  }
  i = parse_thread_id(interp, argv[1]);
  if( i<0 ) return TCL_ERROR;
  if( !threadset[i].busy ){
    Tcl_AppendResult(interp, "no such thread", 0);
    return TCL_ERROR;
  }
  thread_wait(&threadset[i]);
  assert( !threadset[i].db );
  threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]);
  return TCL_OK;
}

/*
** Usage: thread_stmt_get ID
**
** Return the database stmt pointer for the given thread.  Then
** remove the pointer from the thread itself. 
*/
700
701
702
703
704
705
706

707
708
709
710
711
712
713
714
715
716
717
718
     { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
     { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
     { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
     { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
     { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
     { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
     { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     },

     { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   },
  };
  int i;

  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  return TCL_OK;
}
#else
int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
#endif /* SQLITE_OS_UNIX */







>












730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
     { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
     { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
     { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
     { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
     { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
     { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
     { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     },
     { "thread_db_put",     (Tcl_CmdProc*)tcl_thread_db_put     },
     { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   },
  };
  int i;

  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  return TCL_OK;
}
#else
int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
#endif /* SQLITE_OS_UNIX */
Changes to test/thread2.test.
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

207
208
209
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240

do_test thread2-3.20 {
  thread_create A test.db
  thread_compile A {SELECT a FROM t1 LIMIT 3}
  thread_step A
  set STMT [thread_stmt_get A]
  set DB [thread_db_get A]
  thread_halt A
} {}
do_test thread2-3.21 {
  sqlite3_step $STMT
} SQLITE_ROW
do_test thread2-3.22 {
  sqlite3_column_int $STMT 0
} 2
do_test thread2-3.23 {
  # The unlock fails here.  But because we never check the return
  # code from sqlite3OsUnlock (because we cannot do anything about it
  # if it fails) we do not realize that an error has occurred.

  sqlite3_finalize $STMT
} SQLITE_OK
do_test thread2-3.25 {
  sqlite3_close $DB

} SQLITE_OK

do_test thread2-3.30 {
  thread_create A test.db
  thread_compile A {BEGIN}
  thread_step A
  thread_finalize A
  thread_compile A {SELECT a FROM t1 LIMIT 1}
  thread_step A
  thread_finalize A
  set DB [thread_db_get A]
  thread_halt A
} {}
do_test thread2-3.31 {
  set STMT [sqlite3_prepare $DB {INSERT INTO t1 VALUES(99,'error')} -1 TAIL]
  sqlite3_step $STMT
} SQLITE_ERROR
do_test thread2-3.32 {
  sqlite3_finalize $STMT
} SQLITE_MISUSE
do_test thread2-3.33 {
  sqlite3_close $DB

} SQLITE_OK

# VERY important to set the override flag back to its true value.
#
set threadsOverrideEachOthersLocks $orig_threadOverride

# Also important to halt the worker threads, which are using spin
# locks and eating away CPU cycles.







<
<
<









>



|
>
|










<
<
<







|
>
|







188
189
190
191
192
193
194



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

do_test thread2-3.20 {
  thread_create A test.db
  thread_compile A {SELECT a FROM t1 LIMIT 3}
  thread_step A
  set STMT [thread_stmt_get A]
  set DB [thread_db_get A]



  sqlite3_step $STMT
} SQLITE_ROW
do_test thread2-3.22 {
  sqlite3_column_int $STMT 0
} 2
do_test thread2-3.23 {
  # The unlock fails here.  But because we never check the return
  # code from sqlite3OsUnlock (because we cannot do anything about it
  # if it fails) we do not realize that an error has occurred.
  breakpoint
  sqlite3_finalize $STMT
} SQLITE_OK
do_test thread2-3.25 {
  thread_db_put A $DB
  thread_halt A
} {}

do_test thread2-3.30 {
  thread_create A test.db
  thread_compile A {BEGIN}
  thread_step A
  thread_finalize A
  thread_compile A {SELECT a FROM t1 LIMIT 1}
  thread_step A
  thread_finalize A
  set DB [thread_db_get A]



  set STMT [sqlite3_prepare $DB {INSERT INTO t1 VALUES(99,'error')} -1 TAIL]
  sqlite3_step $STMT
} SQLITE_ERROR
do_test thread2-3.32 {
  sqlite3_finalize $STMT
} SQLITE_MISUSE
do_test thread2-3.33 {
  thread_db_put A $DB
  thread_halt A
} {}

# VERY important to set the override flag back to its true value.
#
set threadsOverrideEachOthersLocks $orig_threadOverride

# Also important to halt the worker threads, which are using spin
# locks and eating away CPU cycles.