├── Book-Cover.png ├── LICENSE ├── README.md ├── WF-Book-Sample.pdf ├── src ├── README.md ├── p3_worker.py ├── pipe.py ├── worker.py └── worker_lite.py ├── tests ├── README.md ├── deadlocks │ ├── README.md │ ├── test_lock_deadlock.py │ ├── test_lock_deadlock_1.py │ ├── test_put_get_deadlock.py │ ├── test_put_get_deadlock_1.py │ ├── test_wait_signal_deadlock.py │ └── test_wait_signal_deadlock_1.py ├── test_delay.py ├── test_lock.py ├── test_pipe.py ├── test_put_get.py └── test_wait_signal.py └── unix-windows ├── README.md ├── raspberrypi ├── README.md └── micropython ├── unix ├── GCC1130 │ ├── README.md │ └── micropython ├── GCC940 │ ├── README.md │ └── micropython └── README.md └── windows ├── README.md └── micropython.exe /Book-Cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shariltumin/workers-framework-micropython/b7b8c08dd58298f9bafe779b053b735b558825c8/Book-Cover.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 shariltumin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![book-cover](Book-Cover.png) 2 | # workers-framework-micropython 3 | Workers-Framework: A simpler way to MultiTask in MicroPython 4 | 5 | The link [WF-Book-Sample.pdf](WF-Book-Sample.pdf) will take you to a free sample of the book, which includes the contents, introduction, and appendices. 6 | 7 | The book PDF can be purchased at 8 | [https://leanpub.com/Workers_Framework](https://leanpub.com/Workers_Framework) 9 | if you are interested in purchasing the full version. 10 | 11 | If you'd prefer to read this book as a flipbook online whenever and wherever you want, 12 | go to [https://shariltumin.gumroad.com/l/rbkkbn](https://shariltumin.gumroad.com/l/rbkkbn) and get one. 13 | 14 | What's the big deal? You might be asking me. Let me give you a simple example of sequential versus concurrent execution. 15 | 16 | ``` python 17 | # s_abcd.py - sequential script 18 | def fun(n,m): 19 | for i in range(m): 20 | print(f'{n} at {i}') 21 | 22 | fun('A',3) 23 | fun('B',3) 24 | fun('C',3) 25 | fun('D',3) 26 | 27 | ``` 28 | and 29 | 30 | ``` python 31 | # c_abcd.py - concurrent script 32 | from worker import task, MT 33 | 34 | @task 35 | def fun(p): 36 | n,m = p 37 | c = yield 38 | for i in range(m): 39 | print(f'{n} at {i}') 40 | yield 41 | yield 'OK' 42 | 43 | mt=MT(4) 44 | 45 | mt.worker(fun, ('A',3)) # creat worker 46 | mt.worker(fun, ('B',3)) 47 | mt.worker(fun, ('C',3)) 48 | mt.worker(fun, ('D',3)) 49 | mt.start() # start all workers 50 | print(mt.log()) # check for any error 51 | 52 | ``` 53 | 54 | In the table below, the outputs of these two scripts are shown side by side. 55 | 56 | | s_abcd.py | c_abcd.py | 57 | |---------------------------------|----------------------------| 58 | | A at 0 | A at 0 | 59 | | A at 1 | B at 0 | 60 | | A at 2 | C at 0 | 61 | | B at 0 | D at 0 | 62 | | B at 1 | A at 1 | 63 | | B at 2 | B at 1 | 64 | | C at 0 | C at 1 | 65 | | C at 1 | D at 1 | 66 | | C at 2 | A at 2 | 67 | | D at 0 | B at 2 | 68 | | D at 1 | C at 2 | 69 | | D at 2 | D at 2 | 70 | 71 | 72 | I hope you recognize the significance of these results. 73 | 74 | Instead of four ```for``` loops, how about four ```while True``` loops? *Tasks* running as *workers* will solve the problem nicely. 75 | 76 | 77 | Here's a simple example of using REPL keyboard input to control the rate at which the *Raspberry Pi Pico*'s onboard LED blinks. 78 | 79 | ``` python 80 | 81 | # blink_rp2040.py 82 | from worker import MT, task 83 | import sys, uselect 84 | from machine import Pin 85 | 86 | # poll is better than uselect.select 87 | spoll=uselect.poll() 88 | spoll.register(sys.stdin,uselect.POLLIN) 89 | 90 | @task 91 | def kb(pm): 92 | c=yield 93 | poll=spoll.poll 94 | read=sys.stdin.read 95 | v=c.v 96 | v.D=500 # 0.5 sec delay at start 97 | while True: 98 | if poll(0): 99 | w=read(1) 100 | print('w:', w) 101 | if (w=='+' or w=='f') and v.D<1000: # faster rate 102 | v.D -= 10 103 | elif (w=='-' or w=='s') and v.D>10: # slower rate 104 | v.D += 10 105 | print('c.v.D:', v.D) 106 | yield 107 | 108 | @task 109 | def blink(pm): 110 | p=pm[0] 111 | led = Pin(p, Pin.OUT) 112 | c=yield 113 | v=c.v 114 | while True: 115 | led.toggle() 116 | if v.D>0: # if delay is 0 then the program hang 117 | wait=c.delay(v.D) 118 | while wait():yield 119 | 120 | mt=MT(2) # we need only 2 workers 121 | mt.worker(kb, ()) # worker for keyboard (in) 122 | mt.worker(blink, (25,)) # worker for LED (out) 123 | mt.start() 124 | 125 | ``` 126 | 127 | Here's an example of how we can use UART to connect two esp32 boards. 128 | Three jumper wires will be required. 129 | The two tx and rx pins must be crossed, i.e. the first esp32's tx-pin is connected to the second esp32's rx-pin, and the second esp32's rx-pin is connected to the first esp32's tx-pin. 130 | Connect the GND-pin of the first esp32 to the GND-pin of the second esp32. 131 | These two esp32 boards communicate via serial lines by sending and receiving messages. 132 | 133 | ``` python 134 | 135 | from worker import task, MT 136 | from machine import UART 137 | from urandom import random 138 | 139 | # Need 3 jumper wires 140 | # Connect A:16 to B:17 141 | # A:17 to B:16 142 | # A:GND to B:GND 143 | 144 | uart = UART(2, baudrate=576000, tx=16, rx=17) 145 | name = 'A' # either 'A' or 'B' (or anything you want) 146 | 147 | @task 148 | def get(p): 149 | u=p[0] 150 | c=yield 151 | while True: 152 | if u.any()==0: 153 | yield 154 | else: 155 | m=u.read() 156 | print('GET:', m.decode()) 157 | if m[:2]!=b'##': 158 | mt.worker(rep, (u, m)) 159 | 160 | @task 161 | def rep(p): 162 | u,m=p 163 | c=yield 164 | wait=c.delay(random()*10+200) 165 | while wait(): yield 166 | u.write(b'##'+m.upper()) 167 | yield 'done' 168 | 169 | @task 170 | def put(p): 171 | u,n=p 172 | c=yield 173 | cnt=0 174 | while True: 175 | m=f'The count at {n} now is {cnt}.' 176 | print('PUT:', m) 177 | w=u.write(m) 178 | if w!=len(m): 179 | print(f'Write error: {w} chars written out of {len(m)}') 180 | cnt+=1 181 | wait=c.delay(random()*1000+200) 182 | while wait(): yield 183 | 184 | mt=MT(10) 185 | mt.worker(get, (uart,)) 186 | mt.worker(put, (uart, name)) 187 | mt.start() 188 | 189 | ``` 190 | 191 | 192 | -------------------------------------------------------------------------------- /WF-Book-Sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shariltumin/workers-framework-micropython/b7b8c08dd58298f9bafe779b053b735b558825c8/WF-Book-Sample.pdf -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Worker-Framework modules 2 | 3 | | Module | Description | 4 | | ------------- | ------------- | 5 | | worker_lite.py | A simplified version of the worker module (for limited memory boards) | 6 | | worker.py | The full version of worker module | 7 | | pipe.py | A ring buffer. The producer writes to the tail, the consumer reads from the head | 8 | | p3_worker.py | The full version of worker module for Python3 | 9 | 10 | -------------------------------------------------------------------------------- /src/p3_worker.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) Sharil Tumin 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | """ 27 | 28 | import gc 29 | from datetime import datetime 30 | def task(g): 31 | def job(pm): 32 | return g(pm) 33 | return job 34 | class MT: 35 | def __init__(my, n=20): 36 | my.s = WS(n); my.x='' 37 | def worker(my, f, p): 38 | if repr(f).find('task')>=0: 39 | if K.tail0: 56 | C=K.A[i] 57 | try: 58 | w=C.send(my.s) 59 | except StopIteration: 60 | w=True # Done 61 | except Exception as x: 62 | w=False # Error 63 | my.x=str(C).split()[2]+' '+str(x) 64 | if w!=None: 65 | C.close();del C 66 | #gc.collect() 67 | K.tail-=1 68 | if i==K.tail: i=0 69 | else: K.A[i]=K.A[K.tail] 70 | K.A[K.tail]=nop 71 | else: 72 | i+=1 73 | if i>=K.tail:i=0 74 | return 75 | def log(my): 76 | return my.x 77 | class K: 78 | A=[];tail=0;size=0;W={};M={};L={} 79 | def nop():pass 80 | class WS: 81 | class V: pass 82 | def __init__(my,s): 83 | K.A=[nop]*s; K.size=s 84 | my.v=WS.V() 85 | gc.enable() 86 | def lock(my,l,w=''): 87 | if l in K.L: return False 88 | else: K.L[l]=w; return True 89 | def unlock(my,l,w=''): 90 | if l in K.L and w==K.L[l]: K.L.pop(l); return True 91 | else: return False 92 | def mbox(my,w,t=0): 93 | K.M[w]=None 94 | if t==0: 95 | def d(): 96 | return K.M[w]==None 97 | else: 98 | n=datetime.now() 99 | def d(): 100 | return K.M[w]==None and (datetime.now()-n).total_seconds()=my.s: 34 | return 0 35 | else: 36 | m=my.n 37 | for v in list(w): 38 | my.a[my.t]=v;my.t+=1;my.n+=1 39 | if my.t==my.s:my.t=0 40 | if my.n>=my.s or my.t==my.h: return my.n-m 41 | return my.n-m 42 | def get(my, c=0): 43 | if my.n==0 or c<0: 44 | return '' 45 | else: 46 | if c>my.n or c==0: c=my.n 47 | if my.h+c<=my.s: 48 | w=my.a[my.h:my.h+c] 49 | my.h=(my.h+c)%my.s 50 | else: 51 | w=my.a[my.h:]+my.a[0:c-(my.s-my.h)] 52 | my.h=c-(my.s-my.h) 53 | my.n-=c 54 | if my.n==0: 55 | my.t=my.h 56 | return w 57 | 58 | -------------------------------------------------------------------------------- /src/worker.py: -------------------------------------------------------------------------------- 1 | """ 2 | /* 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) Sharil Tumin 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | """ 27 | 28 | import gc 29 | import utime 30 | def task(g): 31 | def job(pm): 32 | return g(pm) 33 | return job 34 | class MT: 35 | def __init__(my, n=20): 36 | my.s = WS(n); my.x='' 37 | def worker(my, f, p): 38 | if repr(f).find('closure')>=0: 39 | if K.tail0: 56 | C=K.A[i] 57 | try: 58 | w=C.send(my.s) 59 | except StopIteration: 60 | w=True # Done 61 | except Exception as x: 62 | w=False # Error 63 | my.x=str(C).split()[2]+' '+str(x) 64 | if w!=None: 65 | C.close();del C 66 | #gc.collect() 67 | K.tail-=1 68 | if i==K.tail: i=0 69 | else: K.A[i]=K.A[K.tail] 70 | K.A[K.tail]=nop 71 | else: 72 | i+=1 73 | if i>=K.tail:i=0 74 | return 75 | def log(my): 76 | return my.x 77 | class K: 78 | A=[];tail=0;size=0;M={};L={} 79 | def nop():pass 80 | class WS: 81 | class V: pass 82 | def __init__(my,s): 83 | K.A=[nop]*s; K.size=s 84 | my.v=WS.V() 85 | gc.enable() 86 | def lock(my,l,w=''): 87 | if l in K.L: return False 88 | else: K.L[l]=w; return True 89 | def unlock(my,l,w=''): 90 | if l in K.L and w==K.L[l]: K.L.pop(l); return True 91 | else: return False 92 | def mbox(my,w,t=0): 93 | K.M[w]=None 94 | if t==0: 95 | def d(): 96 | return K.M[w]==None 97 | else: 98 | n=utime.ticks_ms() 99 | def d(): 100 | return K.M[w]==None and utime.ticks_diff(utime.ticks_ms(),n)my.tail:i=0 82 | 83 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | 1. test_delay.py - non-blocking delay 4 | 2. test_lock.py - locking (critical region, semaphores) 5 | 3. test_put_get.py - message passing via mailboxes 6 | 4. test_wait_signal.py - events handling using mailboxes 7 | 5. test_pipe.py - message queue 8 | 9 | 10 | Concurrency can result in a deadlock. 11 | The *deadlocks* directory contains some example scripts that may cause deadlocks. 12 | -------------------------------------------------------------------------------- /tests/deadlocks/README.md: -------------------------------------------------------------------------------- 1 | # Beware of deadlocks 2 | 3 | Deadlock is any situation in concurrent computing in which no tasks can proceed because each is waiting for another task, including itself, to take action, 4 | such as sending a message or, more commonly, releasing a lock. 5 | 6 | Each task, however, is blocking other tasks by holding a critical resource. 7 | One solution is to release critical resources after a predetermined timeout. 8 | -------------------------------------------------------------------------------- /tests/deadlocks/test_lock_deadlock.py: -------------------------------------------------------------------------------- 1 | 2 | from worker import task, MT 3 | @task 4 | def test_lock(p): 5 | n,l1,l2,c,w=p 6 | s=yield 7 | while not s.lock(l1, 'XXXX'): 8 | yield 9 | else: 10 | # Critical region 11 | print(n, 'locking', l1) 12 | print(n, 'trying to get', l2) 13 | yield # this will create deadlock 14 | while not s.lock(l2, 'XXXX'): 15 | while w>0: 16 | print(c, end='') 17 | yield 18 | w-=1 19 | print() 20 | print(n, 'giving up on getting', l2) 21 | break 22 | else: 23 | print(n, 'locking', l2) 24 | print(n, 'in', l1, 'and', l2) 25 | print(n, 'leaving.') 26 | s.unlock(l2, 'XXXX') 27 | s.unlock(l1, 'XXXX') 28 | 29 | mt=MT(2) 30 | mt.worker(test_lock, ('ONE', 'L1', 'L2', '-', 5)) 31 | mt.worker(test_lock, ('TWO', 'L2', 'L1', '+', 5)) 32 | mt.start() 33 | 34 | -------------------------------------------------------------------------------- /tests/deadlocks/test_lock_deadlock_1.py: -------------------------------------------------------------------------------- 1 | 2 | from worker import task, MT 3 | from random import random 4 | @task 5 | def test_lock(p): 6 | n,l1,l2,c,w=p 7 | s=yield 8 | while not s.lock(l1, n): 9 | yield 10 | else: 11 | # Critical region 12 | print(n, 'locking', l1) 13 | print(n, 'trying to get', l2) 14 | yield # this will create deadlock 15 | while not s.lock(l2, n): 16 | while w>0: 17 | print(c, end='') 18 | yield 19 | w-=1 20 | print() 21 | print(n, 'giving up on getting', l2) 22 | print(n, 'releasing', l1) 23 | s.unlock(l1, n) 24 | w=int((random()*10)+1) 25 | while w>0: yield; w-=1 26 | mt.worker(test_lock, p) 27 | return 28 | else: 29 | print(n, 'locking', l2) 30 | print(n, 'in', l1, 'and', l2) 31 | print(n, 'leaving.') 32 | s.unlock(l2, n) 33 | s.unlock(l1, n) 34 | 35 | mt=MT(4) 36 | mt.worker(test_lock, ('ONE', 'L1', 'L2', '-', 5)) 37 | mt.worker(test_lock, ('TWO', 'L2', 'L1', '+', 5)) 38 | mt.start() 39 | 40 | -------------------------------------------------------------------------------- /tests/deadlocks/test_put_get_deadlock.py: -------------------------------------------------------------------------------- 1 | 2 | from worker import task, MT 3 | 4 | @task 5 | def X(pm): 6 | w=pm[0] 7 | c=yield 8 | wait=c.mbox('BOX1', w) 9 | print("X waiting for BOX1 from Y") 10 | while wait(): 11 | yield 12 | else: 13 | msg=c.get('BOX1') 14 | if not msg: 15 | print("X giving up on BOX1") 16 | else: 17 | print("X Get BOX1", msg) 18 | c.put('BOX2', (1,True,"X is OK")) 19 | return 20 | 21 | @task 22 | def Y(pm): 23 | w=pm[0] 24 | c=yield 25 | wait=c.mbox('BOX2', w) 26 | print("Y waiting for BOX2 from X") 27 | while wait(): 28 | yield 29 | else: 30 | msg=c.get('BOX2') 31 | if not msg: 32 | print("Y giving up on BOX2") 33 | else: 34 | print("Y Get BOX2", msg) 35 | c.put('BOX1', (2,True,"Y is OK")) 36 | return 37 | 38 | mt=MT() 39 | #mt.worker(X,(500,)) 40 | #mt.worker(Y,(500,)) 41 | mt.worker(X,(0,)) 42 | mt.worker(Y,(0,)) 43 | mt.start() 44 | print(mt.x) 45 | 46 | -------------------------------------------------------------------------------- /tests/deadlocks/test_put_get_deadlock_1.py: -------------------------------------------------------------------------------- 1 | 2 | from worker import task, MT 3 | from random import random 4 | 5 | @task 6 | def X(pm): 7 | w=pm[0] 8 | c=yield 9 | wait=c.mbox('BOX1', w) 10 | print("X waiting for BOX1 from Y") 11 | loop=True 12 | while loop: 13 | #print('x-w', w) 14 | if w==0: 15 | w=int((random()*10)+1) 16 | else: 17 | w=10000 18 | while wait() and w>0: 19 | yield 20 | w-=1 21 | else: 22 | msg=c.get('BOX1') 23 | if not msg: 24 | print("X giving up on BOX1") 25 | w=int((random()*10)+1) 26 | while w>0: yield; w-=1 27 | c.put('BOX2', (1,False,"X giving up")) 28 | yield 29 | else: 30 | print("X Get BOX1", msg) 31 | c.put('BOX2', (1,True,"X is OK")) 32 | loop=False 33 | return 34 | 35 | @task 36 | def Y(pm): 37 | w=pm[0] 38 | c=yield 39 | wait=c.mbox('BOX2', w) 40 | print("Y waiting for BOX2 from X") 41 | loop=True 42 | while loop: 43 | #print('y-w', w) 44 | if w==0: 45 | w=int((random()*10)+1) 46 | else: 47 | w=10000 48 | while wait() and w>0: 49 | yield 50 | w-=1 51 | else: 52 | msg=c.get('BOX2') 53 | if not msg: 54 | print("Y giving up on BOX2") 55 | w=int((random()*10)+1) 56 | while w>0: yield; w-=1 57 | c.put('BOX1', (1,False,"Y giving up")) 58 | yield 59 | else: 60 | print("Y Get BOX2", msg) 61 | c.put('BOX1', (2,True,"Y is OK")) 62 | loop=False 63 | return 64 | 65 | mt=MT() 66 | mt.worker(X,(500,)) 67 | mt.worker(Y,(500,)) 68 | #mt.worker(X,(0,)) 69 | #mt.worker(Y,(0,)) 70 | mt.start() 71 | print(mt.x) 72 | 73 | -------------------------------------------------------------------------------- /tests/deadlocks/test_wait_signal_deadlock.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # test_wait_signal_deadlock.py 4 | from worker import task, MT 5 | 6 | @task 7 | def X(pm): 8 | c=yield; i=0; keep=True 9 | event="T0-event-25" 10 | wait=c.mbox('x1',50) 11 | while wait(): 12 | i+=1 13 | if i%10000==0: 14 | print("X waiting for event on x1", i) 15 | yield 16 | else: 17 | ev=c.get('x1', keep) 18 | print("X get", ev) 19 | if ev==event: 20 | print('Event @X:', c.get('x1')) 21 | c.put('y1', "S0-event-31") 22 | return 23 | 24 | @task 25 | def Y(pm): 26 | c=yield;i=0;keep=True 27 | event="S0-event-31" 28 | wait=c.mbox('y1',50) 29 | while wait(): 30 | i+=1 31 | if i%10000==0: 32 | print("Y waiting for event on y1", i) 33 | yield 34 | else: 35 | ev=c.get('y1', keep) 36 | print("Y get", ev) 37 | if ev==event: 38 | print('Event @Y:', c.get('y1')) 39 | c.put('z1', "R0-event-13") 40 | return 41 | 42 | @task 43 | def Z(pm): 44 | c=yield;i=0;keep=True 45 | event="R0-event-13" 46 | wait=c.mbox('z1',50) 47 | while wait(): 48 | i+=1 49 | if i%10000==0: 50 | print("Z waiting for event on z1", i) 51 | yield 52 | else: 53 | ev=c.get('z1', keep) 54 | print("Z get", ev) 55 | if ev==event: 56 | print('Event @Z:', c.get('z1')) 57 | c.put('x1', "T0-event-25") 58 | return 59 | 60 | mt=MT(3) 61 | mt.worker(X, ()) 62 | mt.worker(Y, ()) 63 | mt.worker(Z, ()) 64 | mt.start() 65 | print(mt.log()) 66 | 67 | -------------------------------------------------------------------------------- /tests/deadlocks/test_wait_signal_deadlock_1.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # test_wait_signal_deadlock.py 4 | from worker import task, MT 5 | from random import random 6 | 7 | @task 8 | def X(pm): 9 | c=yield; i=0; keep=True 10 | event="T0-event-25" 11 | wait=c.mbox('x1',50) 12 | while wait(): 13 | i+=1 14 | if i%10000==0: 15 | print("X waiting for event on x1", i) 16 | yield 17 | else: 18 | ev=c.get('x1', keep) 19 | print("X get", ev) 20 | if ev==event: 21 | print('Event @X:', c.get('x1')) 22 | c.put('y1', "S0-event-31") 23 | return 24 | 25 | @task 26 | def Y(pm): 27 | c=yield;i=0;keep=True 28 | event="S0-event-31" 29 | wait=c.mbox('y1',50) 30 | while wait(): 31 | i+=1 32 | if i%10000==0: 33 | print("Y waiting for event on y1", i) 34 | yield 35 | else: 36 | ev=c.get('y1', keep) 37 | print("Y get", ev) 38 | if ev==event: 39 | print('Event @Y:', c.get('y1')) 40 | c.put('z1', "R0-event-13") 41 | return 42 | 43 | @task 44 | def Z(pm): 45 | c=yield;i=0;keep=True 46 | event="R0-event-13" 47 | wait=c.mbox('z1',50) 48 | while wait(): 49 | i+=1 50 | if i%10000==0: 51 | print("Z waiting for event on z1", i) 52 | yield 53 | else: 54 | ev=c.get('z1', keep) 55 | print("Z get", ev) 56 | if ev==event: 57 | print('Event @Z:', c.get('z1')) 58 | c.put('x1', "T0-event-25") 59 | return 60 | 61 | @task 62 | def H(pm): 63 | c=yield 64 | w=int((random()*10)+1)%3 65 | if w==0: 66 | c.put('y1', "S0-event-31") 67 | elif w==1: 68 | c.put('z1', "R0-event-13") 69 | else: 70 | c.put('x1', "T0-event-25") 71 | return 72 | 73 | mt=MT(4) 74 | mt.worker(X, ()) 75 | mt.worker(Y, ()) 76 | mt.worker(Z, ()) 77 | mt.worker(H, ()) 78 | mt.start() 79 | print(mt.log()) 80 | 81 | -------------------------------------------------------------------------------- /tests/test_delay.py: -------------------------------------------------------------------------------- 1 | 2 | from worker import task, MT 3 | 4 | @task 5 | def test_delay(p): 6 | n,w=p 7 | print(f'{n} start') 8 | s=yield 9 | wait=s.delay(w) 10 | while wait(): 11 | yield 12 | print(f'{n} stop') 13 | 14 | mt=MT(3) 15 | mt.worker(test_delay, ('ONE', 10)) 16 | mt.worker(test_delay, ('TWO', 6)) 17 | mt.worker(test_delay, ('THREE', 3)) 18 | mt.start() 19 | 20 | -------------------------------------------------------------------------------- /tests/test_lock.py: -------------------------------------------------------------------------------- 1 | 2 | from worker import task, MT 3 | 4 | def critical_region(n): 5 | print(f'{n} in critical region') 6 | 7 | @task 8 | def test_lock(p): 9 | n,l,w=p 10 | s=yield 11 | while not s.lock(l, 'XXXX'): 12 | yield 13 | else: 14 | # Critical region 15 | critical_region(n) 16 | wait=s.delay(w) 17 | while wait(): 18 | yield 19 | s.unlock(l, 'XXXX') 20 | 21 | mt=MT(2) 22 | mt.worker(test_lock, ('ONE', 'L', 5)) 23 | mt.worker(test_lock, ('TWO', 'L', 5)) 24 | mt.start() 25 | 26 | -------------------------------------------------------------------------------- /tests/test_pipe.py: -------------------------------------------------------------------------------- 1 | 2 | # test_pipe.py 3 | from worker import task, MT 4 | from pipe import Pipe 5 | 6 | @task 7 | def X(pm): 8 | s=b'' 9 | c=yield; pipe=c.v.pipe 10 | while True: 11 | r=pipe.get() 12 | if len(r)==0: # buffer empty 13 | yield 14 | else: 15 | if len(r)==1 and r==b'\0': # end of message 16 | break 17 | else: 18 | s+=r 19 | yield 20 | print(s.decode()) 21 | return 22 | 23 | @task 24 | def Y(pm): 25 | s='One for the money.\nTwo for the show.\n' 26 | s+='Three to get ready.\nNow go, cat, go.\n' 27 | c=yield; pipe=c.v.pipe 28 | i=0 29 | s=s.encode() # change to bytes 30 | while i /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so (0xb6ef6000) 10 | libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb6e5d000) 11 | libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6e33000) 12 | libffi.so.6 => /usr/lib/arm-linux-gnueabihf/libffi.so.6 (0xb6e1b000) 13 | libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb6e08000) 14 | libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6cba000) 15 | /lib/ld-linux-armhf.so.3 (0xb6f0b000) 16 | libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6c8d000) 17 | 18 | Version information: 19 | ./micropython: 20 | libdl.so.2 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libdl.so.2 21 | libc.so.6 (GLIBC_2.28) => /lib/arm-linux-gnueabihf/libc.so.6 22 | libc.so.6 (GLIBC_2.25) => /lib/arm-linux-gnueabihf/libc.so.6 23 | libc.so.6 (GLIBC_2.17) => /lib/arm-linux-gnueabihf/libc.so.6 24 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 25 | libm.so.6 (GLIBC_2.23) => /lib/arm-linux-gnueabihf/libm.so.6 26 | libm.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libm.so.6 27 | libpthread.so.0 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libpthread.so.0 28 | /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so: 29 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 30 | /lib/arm-linux-gnueabihf/libm.so.6: 31 | ld-linux-armhf.so.3 (GLIBC_2.4) => /lib/ld-linux-armhf.so.3 32 | libc.so.6 (GLIBC_PRIVATE) => /lib/arm-linux-gnueabihf/libc.so.6 33 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 34 | /lib/arm-linux-gnueabihf/libpthread.so.0: 35 | ld-linux-armhf.so.3 (GLIBC_PRIVATE) => /lib/ld-linux-armhf.so.3 36 | ld-linux-armhf.so.3 (GLIBC_2.4) => /lib/ld-linux-armhf.so.3 37 | libc.so.6 (GLIBC_PRIVATE) => /lib/arm-linux-gnueabihf/libc.so.6 38 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 39 | /usr/lib/arm-linux-gnueabihf/libffi.so.6: 40 | ld-linux-armhf.so.3 (GLIBC_2.4) => /lib/ld-linux-armhf.so.3 41 | libgcc_s.so.1 (GCC_3.5) => /lib/arm-linux-gnueabihf/libgcc_s.so.1 42 | libc.so.6 (GLIBC_2.7) => /lib/arm-linux-gnueabihf/libc.so.6 43 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 44 | /lib/arm-linux-gnueabihf/libdl.so.2: 45 | ld-linux-armhf.so.3 (GLIBC_PRIVATE) => /lib/ld-linux-armhf.so.3 46 | ld-linux-armhf.so.3 (GLIBC_2.4) => /lib/ld-linux-armhf.so.3 47 | libc.so.6 (GLIBC_PRIVATE) => /lib/arm-linux-gnueabihf/libc.so.6 48 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 49 | /lib/arm-linux-gnueabihf/libc.so.6: 50 | ld-linux-armhf.so.3 (GLIBC_2.4) => /lib/ld-linux-armhf.so.3 51 | ld-linux-armhf.so.3 (GLIBC_PRIVATE) => /lib/ld-linux-armhf.so.3 52 | /lib/arm-linux-gnueabihf/libgcc_s.so.1: 53 | libc.so.6 (GLIBC_2.4) => /lib/arm-linux-gnueabihf/libc.so.6 54 | 55 | $ ./micropython 56 | MicroPython v1.19.1-782-g699477d12-KaKi5 on 2022-12-23; linux [GCC 8.3.0] version 57 | Use Ctrl-D to exit, Ctrl-E for paste mode 58 | >>> from worker import task, MT 59 | >>> 60 | -------------------------------------------------------------------------------- /unix-windows/raspberrypi/micropython: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shariltumin/workers-framework-micropython/b7b8c08dd58298f9bafe779b053b735b558825c8/unix-windows/raspberrypi/micropython -------------------------------------------------------------------------------- /unix-windows/unix/GCC1130/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | $ ldd -v micropython 3 | linux-vdso.so.1 (0x00007ffc5f9a8000) 4 | libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2b0ee7a000) 5 | libffi.so.8 => /lib/x86_64-linux-gnu/libffi.so.8 (0x00007f2b0ee6d000) 6 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2b0ec45000) 7 | /lib64/ld-linux-x86-64.so.2 (0x00007f2b0f044000) 8 | 9 | Version information: 10 | ./micropython: 11 | libffi.so.8 (LIBFFI_CLOSURE_8.0) => /lib/x86_64-linux-gnu/libffi.so.8 12 | libffi.so.8 (LIBFFI_BASE_8.0) => /lib/x86_64-linux-gnu/libffi.so.8 13 | libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6 14 | libc.so.6 (GLIBC_2.33) => /lib/x86_64-linux-gnu/libc.so.6 15 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 16 | libc.so.6 (GLIBC_2.25) => /lib/x86_64-linux-gnu/libc.so.6 17 | libc.so.6 (GLIBC_2.3) => /lib/x86_64-linux-gnu/libc.so.6 18 | libc.so.6 (GLIBC_2.17) => /lib/x86_64-linux-gnu/libc.so.6 19 | libc.so.6 (GLIBC_2.34) => /lib/x86_64-linux-gnu/libc.so.6 20 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 21 | libm.so.6 (GLIBC_2.23) => /lib/x86_64-linux-gnu/libm.so.6 22 | libm.so.6 (GLIBC_2.29) => /lib/x86_64-linux-gnu/libm.so.6 23 | libm.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libm.so.6 24 | /lib/x86_64-linux-gnu/libm.so.6: 25 | ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 26 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 27 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 28 | libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6 29 | /lib/x86_64-linux-gnu/libffi.so.8: 30 | libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6 31 | libc.so.6 (GLIBC_2.7) => /lib/x86_64-linux-gnu/libc.so.6 32 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 33 | libc.so.6 (GLIBC_2.27) => /lib/x86_64-linux-gnu/libc.so.6 34 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 35 | /lib/x86_64-linux-gnu/libc.so.6: 36 | ld-linux-x86-64.so.2 (GLIBC_2.2.5) => /lib64/ld-linux-x86-64.so.2 37 | ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 38 | ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 39 | 40 | $ ./micropython 41 | MicroPython v1.19.1-764-g9bec52a2f-kaki5 on 2022-12-14; linux [GCC 11.3.0] version 42 | Use Ctrl-D to exit, Ctrl-E for paste mode 43 | >>> 44 | ``` 45 | -------------------------------------------------------------------------------- /unix-windows/unix/GCC1130/micropython: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shariltumin/workers-framework-micropython/b7b8c08dd58298f9bafe779b053b735b558825c8/unix-windows/unix/GCC1130/micropython -------------------------------------------------------------------------------- /unix-windows/unix/GCC940/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | $ ldd -v micropython 3 | linux-vdso.so.1 (0x00007ffce9a35000) 4 | libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3c4c9b4000) 5 | libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3c4c991000) 6 | libffi.so.7 => /lib/x86_64-linux-gnu/libffi.so.7 (0x00007f3c4c985000) 7 | libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3c4c97f000) 8 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3c4c78d000) 9 | /lib64/ld-linux-x86-64.so.2 (0x00007f3c4cbfd000) 10 | 11 | Version information: 12 | ./micropython: 13 | libdl.so.2 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libdl.so.2 14 | libffi.so.7 (LIBFFI_CLOSURE_7.0) => /lib/x86_64-linux-gnu/libffi.so.7 15 | libffi.so.7 (LIBFFI_BASE_7.0) => /lib/x86_64-linux-gnu/libffi.so.7 16 | libpthread.so.0 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libpthread.so.0 17 | libm.so.6 (GLIBC_2.23) => /lib/x86_64-linux-gnu/libm.so.6 18 | libm.so.6 (GLIBC_2.29) => /lib/x86_64-linux-gnu/libm.so.6 19 | libm.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libm.so.6 20 | libc.so.6 (GLIBC_2.25) => /lib/x86_64-linux-gnu/libc.so.6 21 | libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6 22 | libc.so.6 (GLIBC_2.3) => /lib/x86_64-linux-gnu/libc.so.6 23 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 24 | libc.so.6 (GLIBC_2.17) => /lib/x86_64-linux-gnu/libc.so.6 25 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 26 | /lib/x86_64-linux-gnu/libm.so.6: 27 | ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 28 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 29 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 30 | libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6 31 | /lib/x86_64-linux-gnu/libpthread.so.0: 32 | ld-linux-x86-64.so.2 (GLIBC_2.2.5) => /lib64/ld-linux-x86-64.so.2 33 | ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 34 | libc.so.6 (GLIBC_2.7) => /lib/x86_64-linux-gnu/libc.so.6 35 | libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6 36 | libc.so.6 (GLIBC_2.3.2) => /lib/x86_64-linux-gnu/libc.so.6 37 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 38 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 39 | libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6 40 | /lib/x86_64-linux-gnu/libffi.so.7: 41 | libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6 42 | libc.so.6 (GLIBC_2.7) => /lib/x86_64-linux-gnu/libc.so.6 43 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 44 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 45 | /lib/x86_64-linux-gnu/libdl.so.2: 46 | ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 47 | libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6 48 | libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 49 | libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 50 | /lib/x86_64-linux-gnu/libc.so.6: 51 | ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 52 | ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2 53 | 54 | $ ./micropython 55 | MicroPython v1.19.1-740-gbf49a087b-kaki5 on 2022-12-11; linux [GCC 9.4.0] version 56 | Use Ctrl-D to exit, Ctrl-E for paste mode 57 | >>> 58 | ``` 59 | -------------------------------------------------------------------------------- /unix-windows/unix/GCC940/micropython: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shariltumin/workers-framework-micropython/b7b8c08dd58298f9bafe779b053b735b558825c8/unix-windows/unix/GCC940/micropython -------------------------------------------------------------------------------- /unix-windows/unix/README.md: -------------------------------------------------------------------------------- 1 | # MicroPython for Unix 2 | 3 | 1. GCC1130 - compiled with gcc-11.3.0 4 | 2. GCC940 - compiled with gcc-9.4.0 5 | -------------------------------------------------------------------------------- /unix-windows/windows/README.md: -------------------------------------------------------------------------------- 1 | # MicroPython for Windows 2 | 3 | micropython.exe 4 | -------------------------------------------------------------------------------- /unix-windows/windows/micropython.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shariltumin/workers-framework-micropython/b7b8c08dd58298f9bafe779b053b735b558825c8/unix-windows/windows/micropython.exe --------------------------------------------------------------------------------