Hi, I writing with a kind of sneak preview of a change I would like to make to KSharedDataCache (kdelibs/kdecore/util) in KDE 5.
The idea is to use a separate thread to perform the operations that actually
access shared memory, since a good majority of KSharedDataCache bugs are
reported as resolved by the user when they remove the cache file and allow it
to be regenerated.
While it is best to ensure there's absolutely no logic errors in the cache,
that's not the only source of cache corruption (BleachBit being run in a naïve
mode of operation being the other popular source). With this change
KSharedDataCache will be able to unlink the corrupted cache *and* prevent
application crashes.
It utilizes several nuances of what I believe to be fully POSIX/SUSv3-
compliant code:
* The idea that SIG{SEGV,FPE,ILL,BUS} signals are directed to a specific
thread (i.e. the thread that was running the code that invoked those signals).
* The idea that signal masks are per-thread (so that only the signals listed
above would be delivered to the canary thread).
* Oh, and that sigsetjmp/siglongjmp actually work correctly. ;)
With that said I don't think the code uses as many POSIX options as the rest
of KSharedDataCache (e.g. process-shared mutexes as unimplemented on Mac OS X)
but I wanted to give a chance for you guys to test the prototype code
beforehand and let me know if there's problems.
You'll want to compile with something like this:
$CXX -O2 -o sigcatcher -lrt -pthread sigcatcher.cpp
Make sure optimization is enabled to ensure volatile is used where it's
needed. Assuming everything works right you should get output like:
$ ./sigcatcher
Hello, World!
Got a result of 1
Exited thread
$
If you want to see the signal handler in action there's a couple of lines you
can uncomment (just grep for uncomment to find where).
Obviously any other feedback on possible issues is appreciated as well, but
basically I'd like to catch any portability issues early this time instead of
after-the-fact.
Please CC me on any replies as I'm not subscribed.
Regards,
- Michael Pyne/* * Copyright © 2011 Michael Pyne <[email protected]> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include <iostream> #include <errno.h> #include <string.h> #include <semaphore.h> #include <stdlib.h> #include <pthread.h> #include <signal.h> #include <setjmp.h> #include <unistd.h> class KSDCOperation { public: virtual ~KSDCOperation(); virtual void operator()() = 0; }; KSDCOperation::~KSDCOperation() { } struct SigCatcherSharedData { sem_t workSemaphore; volatile int keepLooping; volatile bool resultValid; volatile sig_atomic_t resultLock; KSDCOperation *operation; }; struct SigCatcherSignalHandlerData { sigjmp_buf env; }; pthread_key_t gSigThreadKey; static void memoryFaultHandler(int signal) { std::cout << "Hello from sig handler." << std::endl; // Figure out what KSDC object we need to jump back to. SigCatcherSignalHandlerData *localData = reinterpret_cast<SigCatcherSignalHandlerData *>( pthread_getspecific(gSigThreadKey) ); // Nothing to do now but jump to our pre-arranged error handler. siglongjmp(localData->env, signal); } void* signalCatcherThread(void *in) { SigCatcherSharedData *shared = reinterpret_cast<SigCatcherSharedData *>(in); // Store the information needed for our thread's signal handler now. pthread_key_create(&gSigThreadKey, free); SigCatcherSignalHandlerData *localData = new SigCatcherSignalHandlerData; pthread_setspecific(gSigThreadKey, localData); // Set the signals we'll handle sigset_t handledSignals; // Block all signals sigfillset(&handledSignals); // Except these... sigdelset(&handledSignals, SIGSEGV); sigdelset(&handledSignals, SIGBUS); sigdelset(&handledSignals, SIGFPE); sigdelset(&handledSignals, SIGILL); int err; if ((err = pthread_sigmask(SIG_SETMASK, &handledSignals, 0)) != 0) { std::cerr << "error: pthread_sigmask: " << strerror(err) << std::endl; } // Now establish handlers for signals that are not blocked struct sigaction sigHandler; sigHandler.sa_handler = memoryFaultHandler; sigemptyset(&sigHandler.sa_mask); sigHandler.sa_flags = 0; if ((err = sigaction(SIGSEGV, &sigHandler, 0)) < 0) { std::cerr << "Error: sigaction: " << strerror(err) << std::endl; } if ((err = sigaction(SIGBUS, &sigHandler, 0)) < 0) { std::cerr << "Error: sigaction: " << strerror(err) << std::endl; } if ((err = sigaction(SIGFPE, &sigHandler, 0)) < 0) { std::cerr << "Error: sigaction: " << strerror(err) << std::endl; } if ((err = sigaction(SIGILL, &sigHandler, 0)) < 0) { std::cerr << "Error: sigaction: " << strerror(err) << std::endl; } int jmpResult = sigsetjmp(localData->env, 1); if (jmpResult) { // siglongjmp called std::cerr << "Caught signal: " << jmpResult << std::endl; // Mark whatever operation was going on as failed. shared->resultValid = false; // Ensure we unlock our shared data if we had it locked before. shared->resultLock = 0; pthread_exit(0); } else { // jmpbuf set, continue as normal shared->resultValid = true; while (shared->keepLooping) { while (sem_wait(&shared->workSemaphore) != 0) { if (errno != EINTR) { err = errno; std::cerr << "Error: sem_wait: " << strerror(err) << std::endl; shared->resultValid = false; shared->resultLock = 0; pthread_exit(0); } } // This should only happen if the parent thread has told us to // exit. if (!shared->keepLooping) { pthread_exit(0); } // Actually do the freakin' work (*shared->operation)(); // Uncomment one of these to simulate a crash. //*(reinterpret_cast<int*>(0)) = 5; //volatile int dummy = 1/0; // Free up out parent thread currently spinning. shared->resultLock = 0; } } return 0; } class HelloOperation : public KSDCOperation { public: HelloOperation() : KSDCOperation(), m_done(false) { } virtual void operator()() { std::cout << "Hello, World!" << std::endl; m_done = true; // Result } // Just to show how results would have to be stored in the functor object // itself. bool m_done; }; int main() { SigCatcherSharedData sharedData; sharedData.keepLooping = true; sharedData.resultValid = true; sharedData.operation = 0; int err; if (sem_init(&sharedData.workSemaphore, 0, 0) < 0) { err = errno; std::cerr << "Error: sem_init: " << strerror(err) << std::endl; exit(1); } pthread_t exeThread; if ((err = pthread_create(&exeThread, 0, signalCatcherThread, &sharedData)) < 0) { std::cerr << "Error: pthread_create: " << strerror(err) << std::endl; exit(1); } // For each task to complete, we need to have a KSDCOperation object, and // get our spinlock ready by calling ref. Then we post to the workSemaphore // to free our work thread and spin on resultLock. We must ALWAYS check the // validity of the result however. If invalid the thread has terminated, // though we can re-create and try again. sharedData.resultLock = 1; HelloOperation *op = new HelloOperation; sharedData.operation = op; sem_post(&sharedData.workSemaphore); while(sharedData.resultLock) ; if(sharedData.resultValid) { std::cout << "Got a result of " << op->m_done << std::endl; } else { std::cerr << "WHOA, something happened! :(\n"; pthread_join(exeThread, 0); exit(1); } // Normal termination. No need to use resultLock spinlock here. sharedData.keepLooping = false; sem_post(&sharedData.workSemaphore); pthread_join(exeThread, 0); delete sharedData.operation; std::cout << "Exited thread\n"; return 0; }
signature.asc
Description: This is a digitally signed message part.
_______________________________________________ kde-freebsd mailing list [email protected] https://mail.kde.org/mailman/listinfo/kde-freebsd See also http://freebsd.kde.org/ for latest information
