FreeRTOS / Lab-Project-FreeRTOS-POSIX

This repository contains FreeRTOS+POSIX source code, which could be consumed as a submodule.
MIT License
95 stars 60 forks source link

pthread cancel #5

Open nslowell opened 4 years ago

nslowell commented 4 years ago

Where is pthread_cancel? Why is it not implemented?

yuhui-zheng commented 4 years ago

Hello @nslowell,

Not all interfaces specified by POSIX Threading standard are implemented, since this project is a "lab project" nor we could claim we are "POSIX compliant". Though, if features are needed, we are happy to investigate what it takes to implement. In this case pthread_cancel() implementation is proposed in this PR https://github.com/FreeRTOS/Lab-Project-FreeRTOS-POSIX/pull/3 and some feedback is provided in the last comment. (And you are welcome to submit PRs, happy to take a look.)

cobusve commented 3 years ago

The reason it is not implemented is that cancellation points are exceedingly difficult to decypher. See https://lwn.net/Articles/683118/ for some more information. The TLDR; for that is that threads may only be cancelled when a specific set of conditions are met according to the Posix standard, this is called a cancellation point, one example is that a thread may not be cancelled while it is holding on to a mutex or semaphore, but it gets quite a bit more complex than this.

If you look at this man page you will see the list of functions we will have to change to manage the cancellation points. https://man7.org/linux/man-pages/man7/pthreads.7.html

Once we have completed this list we will be able to do pthread_cancel. It is a LOT of work.

Here is the list from the man-page:

Cancellation points POSIX.1 specifies that certain functions must, and certain other functions may, be cancellation points. If a thread is cancelable, its cancelability type is deferred, and a cancellation request is pending for the thread, then the thread is canceled when it calls a function that is a cancellation point.

   The following functions are required to be cancellation points by
   POSIX.1-2001 and/or POSIX.1-2008:

       accept()
       aio_suspend()
       clock_nanosleep()
       close()
       connect()
       creat()
       fcntl() F_SETLKW
       fdatasync()
       fsync()
       getmsg()
       getpmsg()
       lockf() F_LOCK
       mq_receive()
       mq_send()
       mq_timedreceive()
       mq_timedsend()
       msgrcv()
       msgsnd()
       msync()
       nanosleep()
       open()
       openat() [Added in POSIX.1-2008]
       pause()
       poll()
       pread()
       pselect()
       pthread_cond_timedwait()
       pthread_cond_wait()
       pthread_join()
       pthread_testcancel()
       putmsg()
       putpmsg()
       pwrite()
       read()
       readv()
       recv()
       recvfrom()
       recvmsg()
       select()
       sem_timedwait()
       sem_wait()
       send()
       sendmsg()
       sendto()
       sigpause() [POSIX.1-2001 only (moves to "may" list in POSIX.1-2008)]
       sigsuspend()
       sigtimedwait()
       sigwait()
       sigwaitinfo()
       sleep()
       system()
       tcdrain()
       usleep() [POSIX.1-2001 only (function removed in POSIX.1-2008)]
       wait()
       waitid()
       waitpid()
       write()
       writev()

   The following functions may be cancellation points according to
   POSIX.1-2001 and/or POSIX.1-2008:

       access()
       asctime()
       asctime_r()
       catclose()
       catgets()
       catopen()
       chmod() [Added in POSIX.1-2008]
       chown() [Added in POSIX.1-2008]
       closedir()
       closelog()
       ctermid()
       ctime()
       ctime_r()
       dbm_close()
       dbm_delete()
       dbm_fetch()
       dbm_nextkey()
       dbm_open()
       dbm_store()
       dlclose()
       dlopen()
       dprintf() [Added in POSIX.1-2008]
       endgrent()
       endhostent()
       endnetent()
       endprotoent()
       endpwent()
       endservent()
       endutxent()
       faccessat() [Added in POSIX.1-2008]
       fchmod() [Added in POSIX.1-2008]
       fchmodat() [Added in POSIX.1-2008]
       fchown() [Added in POSIX.1-2008]
       fchownat() [Added in POSIX.1-2008]
       fclose()
       fcntl() (for any value of cmd argument)
       fflush()
       fgetc()
       fgetpos()
       fgets()
       fgetwc()
       fgetws()
       fmtmsg()
       fopen()
       fpathconf()
       fprintf()
       fputc()
       fputs()
       fputwc()
       fputws()
       fread()
       freopen()
       fscanf()
       fseek()
       fseeko()
       fsetpos()
       fstat()
       fstatat() [Added in POSIX.1-2008]
       ftell()
       ftello()
       ftw()
       futimens() [Added in POSIX.1-2008]
       fwprintf()
       fwrite()
       fwscanf()
       getaddrinfo()
       getc()
       getc_unlocked()
       getchar()
       getchar_unlocked()
       getcwd()
       getdate()
       getdelim() [Added in POSIX.1-2008]
       getgrent()
       getgrgid()
       getgrgid_r()
       getgrnam()
       getgrnam_r()
       gethostbyaddr() [SUSv3 only (function removed in POSIX.1-2008)]
       gethostbyname() [SUSv3 only (function removed in POSIX.1-2008)]
       gethostent()
       gethostid()
       gethostname()
       getline() [Added in POSIX.1-2008]
       getlogin()
       getlogin_r()
       getnameinfo()
       getnetbyaddr()
       getnetbyname()
       getnetent()
       getopt() (if opterr is nonzero)
       getprotobyname()
       getprotobynumber()
       getprotoent()
       getpwent()
       getpwnam()
       getpwnam_r()
       getpwuid()
       getpwuid_r()
       gets()
       getservbyname()
       getservbyport()
       getservent()
       getutxent()
       getutxid()
       getutxline()
       getwc()
       getwchar()
       getwd() [SUSv3 only (function removed in POSIX.1-2008)]
       glob()
       iconv_close()
       iconv_open()
       ioctl()
       link()
       linkat() [Added in POSIX.1-2008]
       lio_listio() [Added in POSIX.1-2008]
       localtime()
       localtime_r()
       lockf() [Added in POSIX.1-2008]
       lseek()
       lstat()
       mkdir() [Added in POSIX.1-2008]
       mkdirat() [Added in POSIX.1-2008]
       mkdtemp() [Added in POSIX.1-2008]
       mkfifo() [Added in POSIX.1-2008]
       mkfifoat() [Added in POSIX.1-2008]
       mknod() [Added in POSIX.1-2008]
       mknodat() [Added in POSIX.1-2008]
       mkstemp()
       mktime()
       nftw()
       opendir()
       openlog()
       pathconf()
       pclose()
       perror()
       popen()
       posix_fadvise()
       posix_fallocate()
       posix_madvise()
       posix_openpt()
       posix_spawn()
       posix_spawnp()
       posix_trace_clear()
       posix_trace_close()
       posix_trace_create()
       posix_trace_create_withlog()
       posix_trace_eventtypelist_getnext_id()
       posix_trace_eventtypelist_rewind()
       posix_trace_flush()
       posix_trace_get_attr()
       posix_trace_get_filter()
       posix_trace_get_status()
       posix_trace_getnext_event()
       posix_trace_open()
       posix_trace_rewind()
       posix_trace_set_filter()
       posix_trace_shutdown()
       posix_trace_timedgetnext_event()
       posix_typed_mem_open()
       printf()
       psiginfo() [Added in POSIX.1-2008]
       psignal() [Added in POSIX.1-2008]
       pthread_rwlock_rdlock()
       pthread_rwlock_timedrdlock()
       pthread_rwlock_timedwrlock()
       pthread_rwlock_wrlock()
       putc()
       putc_unlocked()
       putchar()
       putchar_unlocked()
       puts()
       pututxline()
       putwc()
       putwchar()
       readdir()
       readdir_r()
       readlink() [Added in POSIX.1-2008]
       readlinkat() [Added in POSIX.1-2008]
       remove()
       rename()
       renameat() [Added in POSIX.1-2008]
       rewind()
       rewinddir()
       scandir() [Added in POSIX.1-2008]
       scanf()
       seekdir()
       semop()
       setgrent()
       sethostent()
       setnetent()
       setprotoent()
       setpwent()
       setservent()
       setutxent()
       sigpause() [Added in POSIX.1-2008]
       stat()
       strerror()
       strerror_r()
       strftime()
       symlink()
       symlinkat() [Added in POSIX.1-2008]
       sync()
       syslog()
       tmpfile()
       tmpnam()
       ttyname()
       ttyname_r()
       tzset()
       ungetc()
       ungetwc()
       unlink()
       unlinkat() [Added in POSIX.1-2008]
       utime() [Added in POSIX.1-2008]
       utimensat() [Added in POSIX.1-2008]
       utimes() [Added in POSIX.1-2008]
       vdprintf() [Added in POSIX.1-2008]
       vfprintf()
       vfwprintf()
       vprintf()
       vwprintf()
       wcsftime()
       wordexp()
       wprintf()
       wscanf()
yuhui-zheng commented 3 years ago

Since I'm on this thread, here's my opinion.

The design was shared in email some time ago, posting that would be a good starting point to get community contribution. And of course it is up to the FreeRTOS team to define the scope for FreeRTOS-POSIX, as POSIX alone is huge. I'm dropping from this conversation now.

cobusve commented 3 years ago

@yuhui-zheng , I am not sure I understand what you mean with regard to "read/readv" applicability to FreeRTOS above? As you know a "stream" in C is called a "FILE" (for historical reasons) and functions like scanf and printf will typically make use of these IO streams, which seems very relevant to FreeRTOS. We use it with serial ports on devices all of the time?

I am also not sure what you mean by "In this case, what it really takes to support thread cancellation is to update pthread data structure to track thread cancellation state.", but your comments for sure has me hopeful. If you have a suggestion how we could implement this relatively simply (which it sure sounds like you have) then I would love to give it a try!

From what I can gather a call to pthread_cancel must cause a target thread to - at a later point in time, and in the context of the target thread (not the caller) kill itself, including running any pushed cleanup handlers. If the target thread is suspended at the time of the request, due to something like e.g. pthread_cond_wait, the target thread shoud be awakened and it should then cancel itself when next scheduled.

One curiosity here is that pthread_mutex_lock() is NOT a cancellation point, which means that if the target thread is blocked waiting on a mutex you will need a mechanism to unblock it conditionally depending on whether the mutex it is blocked by is a cancellation point mutex or a non-cancellation point mutex.

I am not saying that this is not possible, of course it is possible, but this seems rather hard to implement without doing a lot more than simply tracking cancellation state?