clinuxconcurrencylinked-listsigaction

Walking a TAILQ in a signal handler


I have an application that maintains a list of structures, linked together by a TAILQ. Each structure has a dirty bit, and pointer to some dedicated special pages in memory. I need to know if someone writes to any of these pages, so I mprotect them to PROT_READ, and then install a signal handler to go off when a SEGV is detected.

When the handler gets invoked, I retrieve the address and walk my list to see if the segv occurred on any of my pages, and if it did, I mark the page as dirty, and mprotect it to be writeable. So it looks something like this:

typedef struct _record_t {
     void        * start_addr;
     int           dirty;
     TAILQ_ENTRY(_record_t)
                   tailq_entry;
} record_t;

segv_handler(int sig, siginfo_t *si, void *p1) {
     void       * addr = si->si_addr;
     void       * addr_page = ROUND_DOWN(addr, page_size);
     record_t     rec;
     TAILQ_FOREACH(rec, g_reclist, tailq_entry) {
         if (rec->start_addr == addr_page) {
             rec->dirty = 1;
             mprotect(addr_btm, page_size, PROT_READ|PROT_WRITE);
             return;
         }
     }
     // otherwise call default page handler...
}

I'm concerned about being able to walk the list safely within the signal handler. If one thread is modifying the list when another generates a SEGV, I'm concerned that there will be some undefined behavior.

If I could review the hell out of the thread as it modifies the list, and I know it does does not generate a SEGV, is there any safe way to traverse this list in the signal handler? That is, if I detect that the list is being modified when the SEGV goes off, is there any way to block the signal handler until the other thread completes updating the list?

Finally, one last question -- in certain circumstances it may be necessary to modify the list (add or remove an entry) in the signal handler. Is there a safe way to do this?


Solution

  • I see 2 possible solutions:

    1. signalfd(2)

    Use signalfd(2) and read from the signalfd file descriptor from your main thread.

    This solution will allow you check for and handle the SEGV signal explicitly, so that you can make sure that the linked list is not being modified at the same time.

    2. Lock-free linked list

    Change your linked list to a lock-free implementation so that it is signal-safe. Lock-Free Linked Lists and Skip Lists describes one possible approach.