Skip to content

Instantly share code, notes, and snippets.

@pavelzw
Last active January 20, 2021 16:53
Show Gist options
  • Save pavelzw/a93bd3ff77c8116fa7c21fd070ef2c93 to your computer and use it in GitHub Desktop.
Save pavelzw/a93bd3ff77c8116fa7c21fd070ef2c93 to your computer and use it in GitHub Desktop.
Probability density of a race condition
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
},
"colab": {
"name": "a93bd3ff77c8116fa7c21fd070ef2c93",
"provenance": [],
"include_colab_link": true
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/pavelzw/a93bd3ff77c8116fa7c21fd070ef2c93\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "V7_Q2Rl_j6Ja"
},
"source": [
"This notebook plots the probability density of the result of a race condition. The first thread increments a counter `c` `inner_loop_size` times and the second thread decrements this counter. \n",
"\n",
"Due to the race condition, `c` can take values from `-inner_loop_size` to `inner_loop_size`. This density is plotted below with a sample size of `n = 1000`."
]
},
{
"cell_type": "code",
"metadata": {
"id": "kefe4s6Wj6Jb"
},
"source": [
"import threading\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "DbUWCl72j6Jb",
"outputId": "d75e6816-ae24-4355-b0c5-7b9eb2f26aaa"
},
"source": [
"inner_loop_size = int(1e7)\n",
"\n",
"class RaceCondition:\n",
" def __init__(self):\n",
" self.c = 0\n",
" \n",
" def increment(self):\n",
" for i in range(inner_loop_size):\n",
" self.c += 1\n",
" \n",
" def decrement(self):\n",
" for i in range(inner_loop_size):\n",
" self.c -= 1\n",
"\n",
"n = 1000\n",
"values = np.empty(n)\n",
"racecond = RaceCondition()\n",
"from tqdm import tqdm\n",
"for i in tqdm(range(n)):\n",
" increment = threading.Thread(target=racecond.increment)\n",
" decrement = threading.Thread(target=racecond.decrement)\n",
"\n",
" increment.start()\n",
" decrement.start()\n",
" increment.join()\n",
" decrement.join()\n",
" \n",
" values[i] = racecond.c\n",
" racecond.c = 0"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"100%|██████████| 1000/1000 [35:34<00:00, 2.13s/it]\n"
],
"name": "stderr"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "PBXQsgWzj6Jc"
},
"source": [
"Let's check out the histogram with the estimated density."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 295
},
"id": "i4dibaltj6Jc",
"outputId": "9adc78de-5804-4640-86a9-de31491fee38"
},
"source": [
"import seaborn as sns\n",
"\n",
"sns.histplot(values, bins=100, binrange=(-inner_loop_size, inner_loop_size), kde=True)"
],
"execution_count": 3,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x7fb89da187f0>"
]
},
"metadata": {
"tags": []
},
"execution_count": 3
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment