PTHREAD_CANCEL_ASYNCHRONOUS Cancels the entire process

In a C program, I use PTHREAD_CANCEL_ASYNCHRONOUS to immediately cancel a thread as soon as pthread_cancel is started from the parent thread. But this leads to the failure of the whole process with segmentation error . The task of the child stream is to get some data from the database server. And my logic is that if it does not receive data within 10 seconds, the thread must be killed from the parent thread.

I only want to kill the child thread, not the whole process.

 struct str_thrd_data { SQLHANDLE hstmt; int rc; bool thrd_completed_flag; }; void * str_in_thread_call(void *in_str_arg) { int thrd_rc; struct str_thrd_data *str_arg; str_arg = in_str_arg; thrd_rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); if (thrd_rc != 0) handle_error_en(thrd_rc, "pthread_setcancelstate"); thrd_rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (thrd_rc != 0) handle_error_en(thrd_rc, "pthread_setcancelstate"); thrd_rc = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); if (thrd_rc != 0) handle_error_en(thrd_rc, "pthread_setcanceltype"); // Code to call SQL Dynamic Query from a Database Server. This takes time more than 10 seconds. thrd_rc = SQLExecute(hstmt); printf("\n*********************Normal Thread termination withing timelimit %d\n",str_arg->rc); str_arg->thrd_completed_flag = true; } int main() { printf("\nPJH: New THread created.\n"); pthread_attr_t tattr; pthread_t th; size_t mysize = 1; struct str_thrd_data atd; atd.hstmt = hstmt; atd.rc= rc; atd.thrd_completed_flag = false; thrd_rc = pthread_attr_init(&tattr); thrd_rc = pthread_attr_setstacksize(&tattr, mysize); thrd_rc = pthread_create(&th, &tattr, &str_in_thread_call, &atd); if (thrd_rc != 0) handle_error_en(thrd_rc, "pthread_create"); // While Loop tp count till 10 seconds. while(timeout !=0) { printf("%d Value of rc=%d\n",timeout, atd.rc); if(atd.rc != 999) break; timeout--; usleep(10000); } rc = atd.rc; //Condition to check if thread is completed or not yet. if(atd.thrd_completed_flag == false) { //Thread not comepleted within time, so Kill it now. printf("PJH ------- 10 Seconds Over\n"); thrd_rc = pthread_cancel(th); printf("PJH ------- Thread Cancelled Immediately \n"); if (thrd_rc != 0) { handle_error_en(thrd_rc, "pthread_cancel"); } printf("\nPJH &&&&&&&& Thread Cancelled Manually\n"); } thrd_rc = pthread_join(th,NULL); // some other job ..... } 

gdb process_name corefile shows the backtrace below: - Basically all the functions of the SQL Library.

 #0 0xffffe410 in __kernel_vsyscall () #1 0x0059fe30 in raise () from /lib/libc.so.6 #2 0x005a1741 in abort () from /lib/libc.so.6 #3 0xdef3f5d7 in ?? () from /usr/lib/libstdc++.so.5 #4 0xdef3f624 in std::terminate() () from /usr/lib/libstdc++.so.5 #5 0xdef3f44c in __gxx_personality_v0 () from /usr/lib/libstdc++.so.5 #6 0x007e1917 in ?? () from /lib/libgcc_s.so.1 #7 0x007e1c70 in _Unwind_ForcedUnwind () from /lib/libgcc_s.so.1 #8 0x007cda46 in _Unwind_ForcedUnwind () from /lib/libpthread.so.0 #9 0x007cb471 in __pthread_unwind () from /lib/libpthread.so.0 #10 0x007c347a in sigcancel_handler () from /lib/libpthread.so.0 #11 <signal handler called> #12 0xffffe410 in __kernel_vsyscall () #13 0x0064decb in semop () from /lib/libc.so.6 #14 0xe0245901 in sqloSSemP () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #15 0xe01e7f3c in sqlccipcrecv(sqlcc_comhandle*, sqlcc_cond*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #16 0xe03fe135 in sqlccrecv () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #17 0xe02a0307 in sqljcReceive(sqljCmnMgr*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #18 0xe02d0ba3 in sqljrReceive(sqljrDrdaArCb*, db2UCinterface*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #19 0xe02c510d in sqljrDrdaArExecute(db2UCinterface*, UCstpInfo*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #20 0xe01392bc in CLI_sqlCallProcedure(CLI_STATEMENTINFO*, CLI_ERRORHEADERINFO*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #21 0xe00589c7 in SQLExecute2(CLI_STATEMENTINFO*, CLI_ERRORHEADERINFO*) () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #22 0xe0050fc9 in SQLExecute () from /opt/IBM/db2/V9.1/lib32/libdb2.so.1 #23 0x080a81f7 in apcd_in_thread_call (in_apcd_arg=0xbc8e8f34) at dcs_db2_execute.c:357 #24 0x007c4912 in start_thread () from /lib/libpthread.so.0 #25 0x0064c60e in clone () from /lib/libc.so.6 
+4
source share
2 answers

Canceling an asynchronous thread can only be safely used for threads that perform a very limited set of operations — official rules are long and confusing, but in reality, threads subject to asynchronous failures can only perform pure computation. They cannot do I / O, they cannot allocate memory, they cannot accept locks of any type, and they cannot call any library function that could do any of the above. You cannot use async safely to cancel a thread that speaks to the database.

Deferred cancellations are less limited but still extremely complex. If your database library is not encoded to cope with the possibility that the calling thread might be canceled in the middle of the operation — and probably not — then you also cannot safely use deferred cancellation.

You will need to find another mechanism to interrupt requests that take too long to complete.

EDIT:. Since this is a DB2 and API with a SqlSetStmtAttr CLI name, try using SqlSetStmtAttr to set the SQL_ATTR_QUERY_TIMEOUT parameter in a prepared statement. This is a complete list of parameters that can be set in this way , and here is another discussion of request timeouts .

SON OF EDIT:. According to a friend who did a lot more work with the database than I do, it is likely that there is a server mechanism for canceling slow requests regardless of their source. If this exists in DB2, it may be more convenient than manually setting timeouts on all your requests on the client side, especially since it can log slow requests so that you know which ones can optimize them.

+8
source

Since the client code for the database is probably not written in such a way that it can deal with cancellation (most library code is not), I don't think this approach will work. For more details see section Zak.

If you need to cancel connections to the database, you probably have to proxy the connection and kill the proxy. Basically, you should create a second thread that listens on the port and redirects the connection to the database server, and direct the database client to connect to this port on the local host instead of the real database server / port. The proxy stream can then be canceled (with normal delayed cancellation rather than asynchronous), with a cancellation clearing handler to disable sockets. Losing the connection to the database server through a private socket (and not just a non-sensitive socket) should result in an error returning the database client library code, and you can also close the stream.

Keep in mind that when setting up such a proxy server, you need to make sure that you are not introducing security problems with access to the database.

Here is a sketch of the code that you could use for a proxy, without error checking logic and without any explanation for unexpected clients connecting:

 int s, c; struct addrinfo *ai; struct sockaddr_in sa; char portstr[8]; getaddrinfo(0, 0, &(struct addrinfo){ .ai_flags = AI_PASSIVE, .ai_family = AF_INET }, &ai); s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); bind(s, ai->ai_addr, ai_addrlen); freeaddrinfo(ai); getsockname(s, (void *)&sa, &(socklen_t){sizeof sa}); port = ntohs(sa.sin_port); /* Here, do something to pass the port (assigned by kernel) back to the caller. */ listen(s, 1); c = accept(s, &sa, &(socklen_t){sizeof sa}); close(s); getaddrinfo("dbserver", "dbport", 0, &ai); s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); connect(s, ai->ai_addr, ai->ai_addrlen); freeaddrinfo(ai); 

At this point, you have two sockets, s connected to the database server, and c connected to the database client in another thread in your program. Everything that you read from one must be written to another; use poll to determine which one is ready to be read or written.

During the above installation code, the cancellation should be blocked, except for accept and connect calls, and at these points you need the appropriate cleanup handlers to close your sockets and call freeaddrinfo if the cancellation occurs. It might make sense to copy the data you use from getaddrinfo into local variables so that you can freeaddrinfo before blocking calls and not worry about doing this from the undo clearing handler.

+2
source

All Articles