{"ref":"refs/heads/topic-threads","push_id":55201415,"commits":[{"sha":"ff018588e4819ced3c3064d3d3cec510d182f108","author":{"email":"4db06f45642e8233ebfc9afeecbd0085db05306a@gmail.com","name":"jazzyb"},"url":"https://api.github.com/repos/jazzyb/libforchess/commits/ff018588e4819ced3c3064d3d3cec510d182f108","message":"Fixes failing test from prior commit (af66db0b96).\n\nLet me tell you a story about the strangest bug I ever had to fix:\n\nIt started with tests failing sometimes due to segfaults and sometimes\nfloating point exceptions.  And even occationally, the test would pass\nwithout a hitch.  When I initially ran the code in GDB, assertions would\nfail in the next test case (AI) but no errors were generated.\nEventually I dinked around in GDB long enough trying to figure out what\nwas wrong that I saw a line printed to the terminal from the AI tests,\nand immediately after a segfault in one of the threads from the thread\ntests which, I thought, should have already finished.\n\nAh ha! I thought.  I'm calling pthread_cancel to end the threads, but it\nlooks like I can't trust it.  The threads are continuing to run beyond\ntheir expected lifetime, and the stack (and by extension, the structures\nthey depended on) were changing out from under them.  Sometimes this\nmeant that the thread would sefault or that I would get a floating point\nexception (from the modulus operator failing for some reason), and\nsometimes this meant that the boards on the AI stack were being changed\nunderneath the AI which caused the assertions being thrown.\n\nSo I changed the pthread_cancel calls to pthread_kill calls and created\nsignal handlers for all the threads.  When the thread was sent a\nSIGUSR2, the handler would be invoked which simply called pthread_exit.\nMaybe I could trust this implementation, I thought.  And indeed, the\nfirst few tests ran fine, but the seventh or eighth time I ran 'make\nclean check', the test hung.  Check wasn't very helpful at telling me\nexactly where, and since I didn't know if something in one of the child\nthreads was failing or if it was the main thread, I decided to sprinkle\nprintfs and fflushes throughout and narrow the hanging line down.\n\nI finally discovered that the pthread_mutex_destroy call in\nfc_tpool_free was what was hanging.  Doing some research I discovered\nthat calling pthread_mutex_destroy on a locked mutex resulted in\nundefined behavior.  What I figure was happening was that a thread was\nlocking the mutex and then getting the signal interrupt.  When\npthread_mutex_destroy was then called on the locked thread, it hung.\nPerhaps because the Mac's implementation of pthread_mutex_destroy tries\nto acquire the lock before freeing it; I can't say.  Pthread_cancel must\nhave been handling some clean-up of the threads/mutexes before exiting.\n\nSo I reimplemented fc_tpool_kill_threads with pthread_cancel and put a\nsleep(2) in the failing/erroring test case to give the test threads\nenough time to quit before calling the next test.  I understand that\nthis is a hack; however, fc_tpool_kill_threads is not essential to\nlibforchess at this time, so I can afford it right now.  Plus I intend\nto refactor the thread code next based on what I'm reading in O'Reilly's\nPthreads Programming and Principles of Concurrent and Distributed\nProgramming by M. Ben-Ari, so a great deal of the implmentation will\nprobably change anyway.\n\nMorals of the story:\n(1) Read the manual.  Even if a function call seems self-explanatory\n    like pthread_mutex_destroy, you should still bother to check the man\n    page for any caveats, known bugs, or undefined behavior.\n(2) Undefined behavior means exactly that.  You can't trust it nor can\n    you fully anticipate what it will do.  You may think that it just\n    means that your program may crash, but it could instead hang\n    unpredictably.  It could also download child porn to your computer\n    and anonymously notify the authorities.  You just can't know for\n    sure, so it's best to avoid it altogether.\n\nTHE END\n\nP.S. What made these bugs so difficult to track down was that I started\ntrying to find them by commenting out certain lines of the code (such as\nfc_tpool_kill_threads) which did not result in a hung test and initially\nmade me think that something in fc_tpool_kill_threads was hanging\ninstead of fc_tpool_free."}],"head":"ff018588e4819ced3c3064d3d3cec510d182f108","size":1}