… | |
… | |
2078 | is that the author does not know of a simple (or any) algorithm for a |
2078 | is that the author does not know of a simple (or any) algorithm for a |
2079 | multiple-writer-single-reader queue that works in all cases and doesn't |
2079 | multiple-writer-single-reader queue that works in all cases and doesn't |
2080 | need elaborate support such as pthreads. |
2080 | need elaborate support such as pthreads. |
2081 | |
2081 | |
2082 | That means that if you want to queue data, you have to provide your own |
2082 | That means that if you want to queue data, you have to provide your own |
2083 | queue. And here is how you would implement locking: |
2083 | queue. But at least I can tell you would implement locking around your |
|
|
2084 | queue: |
2084 | |
2085 | |
2085 | =over 4 |
2086 | =over 4 |
2086 | |
2087 | |
2087 | =item queueing from a signal handler context |
2088 | =item queueing from a signal handler context |
2088 | |
2089 | |
… | |
… | |
2125 | |
2126 | |
2126 | =item queueing from a thread context |
2127 | =item queueing from a thread context |
2127 | |
2128 | |
2128 | The strategy for threads is different, as you cannot (easily) block |
2129 | The strategy for threads is different, as you cannot (easily) block |
2129 | threads but you can easily preempt them, so to queue safely you need to |
2130 | threads but you can easily preempt them, so to queue safely you need to |
2130 | emply a traditional mutex lock, such as in this pthread example: |
2131 | employ a traditional mutex lock, such as in this pthread example: |
2131 | |
2132 | |
2132 | static ev_async mysig; |
2133 | static ev_async mysig; |
2133 | static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; |
2134 | static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; |
2134 | |
2135 | |
2135 | static void |
2136 | static void |
… | |
… | |
2697 | =item EV_ATOMIC_T |
2698 | =item EV_ATOMIC_T |
2698 | |
2699 | |
2699 | Libev requires an integer type (suitable for storing C<0> or C<1>) whose |
2700 | Libev requires an integer type (suitable for storing C<0> or C<1>) whose |
2700 | access is atomic with respect to other threads or signal contexts. No such |
2701 | access is atomic with respect to other threads or signal contexts. No such |
2701 | type is easily found in the C language, so you can provide your own type |
2702 | type is easily found in the C language, so you can provide your own type |
2702 | that you know is safe for your purposes. |
2703 | that you know is safe for your purposes. It is used both for signal handler "locking" |
|
|
2704 | as well as for signal and thread safety in C<ev_async> watchers. |
2703 | |
2705 | |
2704 | In the absense of this define, libev will use C<sig_atomic_t volatile> |
2706 | In the absense of this define, libev will use C<sig_atomic_t volatile> |
2705 | (from F<signal.h>), which is usually good enough on most platforms. |
2707 | (from F<signal.h>), which is usually good enough on most platforms. |
2706 | |
2708 | |
2707 | =item EV_H |
2709 | =item EV_H |
… | |
… | |
2911 | =item Changing timer/periodic watchers (by autorepeat or calling again): O(log skipped_other_timers) |
2913 | =item Changing timer/periodic watchers (by autorepeat or calling again): O(log skipped_other_timers) |
2912 | |
2914 | |
2913 | That means that changing a timer costs less than removing/adding them |
2915 | That means that changing a timer costs less than removing/adding them |
2914 | as only the relative motion in the event queue has to be paid for. |
2916 | as only the relative motion in the event queue has to be paid for. |
2915 | |
2917 | |
2916 | =item Starting io/check/prepare/idle/signal/child watchers: O(1) |
2918 | =item Starting io/check/prepare/idle/signal/child/fork/async watchers: O(1) |
2917 | |
2919 | |
2918 | These just add the watcher into an array or at the head of a list. |
2920 | These just add the watcher into an array or at the head of a list. |
2919 | |
2921 | |
2920 | =item Stopping check/prepare/idle watchers: O(1) |
2922 | =item Stopping check/prepare/idle/fork/async watchers: O(1) |
2921 | |
2923 | |
2922 | =item Stopping an io/signal/child watcher: O(number_of_watchers_for_this_(fd/signal/pid % EV_PID_HASHSIZE)) |
2924 | =item Stopping an io/signal/child watcher: O(number_of_watchers_for_this_(fd/signal/pid % EV_PID_HASHSIZE)) |
2923 | |
2925 | |
2924 | These watchers are stored in lists then need to be walked to find the |
2926 | These watchers are stored in lists then need to be walked to find the |
2925 | correct watcher to remove. The lists are usually short (you don't usually |
2927 | correct watcher to remove. The lists are usually short (you don't usually |
… | |
… | |
2941 | =item Priority handling: O(number_of_priorities) |
2943 | =item Priority handling: O(number_of_priorities) |
2942 | |
2944 | |
2943 | Priorities are implemented by allocating some space for each |
2945 | Priorities are implemented by allocating some space for each |
2944 | priority. When doing priority-based operations, libev usually has to |
2946 | priority. When doing priority-based operations, libev usually has to |
2945 | linearly search all the priorities, but starting/stopping and activating |
2947 | linearly search all the priorities, but starting/stopping and activating |
2946 | watchers becomes O(1) w.r.t. prioritiy handling. |
2948 | watchers becomes O(1) w.r.t. priority handling. |
|
|
2949 | |
|
|
2950 | =item Sending an ev_async: O(1) |
|
|
2951 | |
|
|
2952 | =item Processing ev_async_send: O(number_of_async_watchers) |
|
|
2953 | |
|
|
2954 | =item Processing signals: O(max_signal_number) |
|
|
2955 | |
|
|
2956 | Sending involves a syscall I<iff> there were no other C<ev_async_send> |
|
|
2957 | calls in the current loop iteration. Checking for async and signal events |
|
|
2958 | involves iterating over all running async watchers or all signal numbers. |
2947 | |
2959 | |
2948 | =back |
2960 | =back |
2949 | |
2961 | |
2950 | |
2962 | |
2951 | =head1 Win32 platform limitations and workarounds |
2963 | =head1 Win32 platform limitations and workarounds |