Skip to content

Instantly share code, notes, and snippets.

@0xpizza
Last active November 16, 2020 06:25
Show Gist options
  • Select an option

  • Save 0xpizza/81e186af7682f2c4ad7804cc6592f3c2 to your computer and use it in GitHub Desktop.

Select an option

Save 0xpizza/81e186af7682f2c4ad7804cc6592f3c2 to your computer and use it in GitHub Desktop.
Examine how python threads operate when the GIL is released.
import secrets
import hashlib
import tkinter as tk
from tkinter.ttk import Progressbar
import concurrent.futures
def scrypt(pw, salt=None):
assert isinstance(pw, bytes)
if salt is None:
salt = secrets.token_bytes(16)
hash = hashlib.scrypt(
pw, salt=salt,
n=2**20, p=1, r=8,
maxmem=0x7FFFFFFF, dklen=8
)
return hash, salt
class Root(tk.Tk):
def __init__(self):
super().__init__()
f = tk.Frame(self)
self.e = tk.Entry(self)
self.e.pack()
self.e.bind('<Return>', self.do_hash)
self.e.focus_set()
self.executor = concurrent.futures.ThreadPoolExecutor()
self.pbar = Progressbar(self)
self.pbar.config(mode='indeterminate')
def do_hash(self, _event=None):
pw = self.e.get().encode()
# this will block the current python thread.
# tkinter will freeze because it has no CPU time
print('doing blocking')
scrypt(pw)
# this does NOT block because hashlib releases the GIL,
# making this thread a true thread that does not interfere
# with the python state until it has completed.
print('doing non-blocking')
fut = self.executor.submit(scrypt, pw)
fut.add_done_callback(self._finish_hash)
self.e.configure(state=tk.DISABLED)
self.pbar.start()
self.pbar.pack()
def _finish_hash(self, future):
self.pbar.stop()
self.e.config(state=tk.NORMAL)
self.pbar.pack_forget()
if __name__ == '__main__':
Root().mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment