4040#include <esp_log.h>
4141#include <esp_partition.h>
4242#include <limits.h>
43+ #include <pthread.h>
4344#include <stdint.h>
4445#include <sys/socket.h>
4546
46- // introduced starting with 4.4
4747#if ESP_IDF_VERSION_MAJOR >= 5
4848#include "esp_chip_info.h"
4949#endif
5050
51+ #include <esp_vfs_eventfd.h>
52+
5153#ifdef HAVE_SOC_CPU_CORES_NUM
5254#include "soc/soc_caps.h"
5355#endif
6163
6264static Context * port_driver_create_port (const char * port_name , GlobalContext * global , term opts );
6365
66+ static void * select_thread_loop (void * );
67+ static void select_thread_signal (struct ESP32PlatformData * platform );
68+
6469// clang-format off
6570static const char * const esp_free_heap_size_atom = "\x14" "esp32_free_heap_size" ;
6671static const char * const esp_largest_free_block_atom = "\x18" "esp32_largest_free_block" ;
@@ -182,6 +187,29 @@ void sys_init_platform(GlobalContext *glb)
182187{
183188 struct ESP32PlatformData * platform = malloc (sizeof (struct ESP32PlatformData ));
184189 glb -> platform_data = platform ;
190+ platform -> select_thread_exit = false;
191+ platform -> select_events_poll_count = -1 ;
192+ esp_vfs_eventfd_config_t eventfd_config = ESP_VFS_EVENTD_CONFIG_DEFAULT ();
193+ esp_err_t err = esp_vfs_eventfd_register (& eventfd_config );
194+ if (err == ESP_OK ) {
195+ platform -> eventfd_registered = true;
196+ } else {
197+ if (UNLIKELY (err != ESP_ERR_INVALID_STATE )) {
198+ // Function can return fatal ESP_ERR_NO_MEM
199+ fprintf (stderr , "Cannot register eventfd, unexpected error = %d\n" , err );
200+ AVM_ABORT ();
201+ }
202+ platform -> eventfd_registered = false;
203+ }
204+ int signal_fd = eventfd (0 , 0 );
205+ if (UNLIKELY (signal_fd < 0 )) {
206+ fprintf (stderr , "Cannot create signal_fd\n" );
207+ AVM_ABORT ();
208+ }
209+ platform -> signal_fd = signal_fd ;
210+ if (UNLIKELY (pthread_create (& platform -> select_thread , NULL , select_thread_loop , glb ))) {
211+ AVM_ABORT ();
212+ }
185213#ifndef AVM_NO_SMP
186214 // Use the ESP-IDF API to change the default thread attributes
187215 // We use the current main thread priority.
@@ -208,6 +236,16 @@ void sys_init_platform(GlobalContext *glb)
208236void sys_free_platform (GlobalContext * glb )
209237{
210238 struct ESP32PlatformData * platform = glb -> platform_data ;
239+ platform -> select_thread_exit = true;
240+ select_thread_signal (platform );
241+ pthread_join (platform -> select_thread , NULL );
242+ close (platform -> signal_fd );
243+ if (platform -> eventfd_registered ) {
244+ if (UNLIKELY (esp_vfs_eventfd_unregister () != ESP_OK )) {
245+ fprintf (stderr , "Cannot unregister eventfd\n" );
246+ AVM_ABORT ();
247+ }
248+ }
211249 free (platform );
212250}
213251
@@ -560,16 +598,119 @@ void sys_unregister_listener(GlobalContext *global, struct EventListener *listen
560598
561599void sys_register_select_event (GlobalContext * global , ErlNifEvent event , bool is_write )
562600{
563- UNUSED (global );
564601 UNUSED (event );
565602 UNUSED (is_write );
603+
604+ struct ESP32PlatformData * platform = global -> platform_data ;
605+ platform -> select_events_poll_count = -1 ;
606+ select_thread_signal (platform );
566607}
567608
568609void sys_unregister_select_event (GlobalContext * global , ErlNifEvent event , bool is_write )
569610{
570- UNUSED (global );
571611 UNUSED (event );
572612 UNUSED (is_write );
613+
614+ struct ESP32PlatformData * platform = global -> platform_data ;
615+ platform -> select_events_poll_count = -1 ;
616+ select_thread_signal (platform );
617+ }
618+
619+ static void * select_thread_loop (void * arg )
620+ {
621+ GlobalContext * glb = arg ;
622+ struct ESP32PlatformData * platform = glb -> platform_data ;
623+ struct pollfd * fds = malloc (0 );
624+ while (!platform -> select_thread_exit ) {
625+ int select_events_poll_count = platform -> select_events_poll_count ;
626+ int poll_count = 1 ;
627+ int fd_index ;
628+ if (select_events_poll_count < 0 ) {
629+ // Means it is dirty and should be rebuilt.
630+ size_t select_events_new_count ;
631+ if (select_events_poll_count < 0 ) {
632+ select_event_count_and_destroy_closed (NULL , NULL , & select_events_new_count , glb );
633+ } else {
634+ select_events_new_count = select_events_poll_count ;
635+ }
636+
637+ fds = realloc (fds , sizeof (struct pollfd ) * (poll_count + select_events_new_count ));
638+
639+ fds [0 ].fd = platform -> signal_fd ;
640+ fds [0 ].events = POLLIN ;
641+ fds [0 ].revents = 0 ;
642+
643+ fd_index = poll_count ;
644+
645+ struct ListHead * item ;
646+ struct ListHead * select_events = synclist_rdlock (& glb -> select_events );
647+ LIST_FOR_EACH (item , select_events ) {
648+ struct SelectEvent * select_event = GET_LIST_ENTRY (item , struct SelectEvent , head );
649+ if (select_event -> read || select_event -> write ) {
650+ fds [fd_index ].fd = select_event -> event ;
651+ fds [fd_index ].events = (select_event -> read ? POLLIN : 0 ) | (select_event -> write ? POLLOUT : 0 );
652+ fds [fd_index ].revents = 0 ;
653+
654+ fd_index ++ ;
655+ }
656+ }
657+ synclist_unlock (& glb -> select_events );
658+
659+ select_events_poll_count = select_events_new_count ;
660+ platform -> select_events_poll_count = select_events_new_count ;
661+ }
662+
663+ poll_count += select_events_poll_count ;
664+
665+ int nb_descriptors = poll (fds , poll_count , -1 );
666+ fd_index = 0 ;
667+ if (nb_descriptors > 0 ) {
668+ if ((fds [0 ].revents & fds [0 ].events )) {
669+ // We've been signaled
670+ uint64_t ignored ;
671+ if (UNLIKELY (read (platform -> signal_fd , & ignored , sizeof (ignored )) < 0 )) {
672+ fprintf (stderr , "Reading event_fd failed -- errno = %d\n" , errno );
673+ AVM_ABORT ();
674+ }
675+ nb_descriptors -- ;
676+ }
677+ fd_index ++ ;
678+ }
679+
680+ for (int i = 0 ; i < select_events_poll_count && nb_descriptors > 0 ; i ++ , fd_index ++ ) {
681+ if (!(fds [fd_index ].revents & fds [fd_index ].events )) {
682+ continue ;
683+ }
684+ bool is_read = fds [fd_index ].revents & POLLIN ;
685+ bool is_write = fds [fd_index ].revents & POLLOUT ;
686+ fds [fd_index ].revents = 0 ;
687+ nb_descriptors -- ;
688+
689+ select_event_notify (fds [fd_index ].fd , is_read , is_write , glb );
690+ }
691+ }
692+
693+ free ((void * ) fds );
694+
695+ return NULL ;
696+ }
697+
698+ static void select_thread_signal (struct ESP32PlatformData * platform )
699+ {
700+ // Write can fail if the counter overflows
701+ // (very unlikely, 2^64)
702+ uint64_t val = 1 ;
703+ if (UNLIKELY (write (platform -> signal_fd , & val , sizeof (val )) < 0 )) {
704+ uint64_t ignored ;
705+ if (UNLIKELY (read (platform -> signal_fd , & ignored , sizeof (ignored )) < 0 )) {
706+ fprintf (stderr , "Reading event_fd failed\n" );
707+ AVM_ABORT ();
708+ }
709+ if (UNLIKELY (write (platform -> signal_fd , & val , sizeof (val )) < 0 )) {
710+ fprintf (stderr , "Writing event_fd failed\n" );
711+ AVM_ABORT ();
712+ }
713+ }
573714}
574715
575716bool event_listener_is_event (EventListener * listener , listener_event_t event )
0 commit comments