Skip to content

The format context mananger is not threadsafe #83

Open
@walrusVision

Description

@walrusVision

Using the format context manager in a threaded environment introduces a race condition where the global formatter and global plural flag can be reset by one context manager after another context manager has set it, and before it has formatted a string within that context.

A solution to this is bitmath should either make sure that the context manager is threadsafe, or warn in the documentation that the context manager isn't threadsafe and that a user should use the Bitmath object's format function directly instead.

How to REPRODUCE the issue:
This test case can reproduce the issue. The test is only checking format_string but a similar method could be used to test format_plural

#! /usr/bin/env python

from __future__ import print_function

import random
import queue
import threading
import time
import unittest

import bitmath

default_format = bitmath.format_string
bitmath.format_string = "{not_in_context_manager}"

THREAD_COUNT = 8

def format_string(event, err_queue):
    size = bitmath.KiB(0)
    try:
        with bitmath.format(fmt_str=default_format):
            event.wait()
            time.sleep(random.random() * 2)
            str(size)
    except Exception as e:
        err_queue.put(e)


class TestThreading(unittest.TestCase):

    def test_context_manager(self):
        event = threading.Event()
        err_queue = queue.Queue()
        threads = []
        for i in xrange(THREAD_COUNT):
            threads.append(threading.Thread(target=format_string, args=(event, err_queue)))
        for thread in threads:
            thread.start()
        time.sleep(1)
        event.set()
        for thread in threads:
            thread.join()

        error = err_queue.get()
        if error:
            raise error

if __name__ == '__main__':
    unittest.main()

And will produce the following error

======================================================================
ERROR: test_context_manager (__main__.TestThreading)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test_threading.py", line 47, in test_context_manager
    raise error
KeyError: 'not_in_context_manager'

How REPRODUCIBLE (every time? intermittently? only in certain environments?):
In general the the error would only happen in threaded environments where there is a requirement to override the default formatter using the context manager. In our case it was happening intermittently when testing a threaded part of our application which was logging the size of bitmath objects

What you EXPECTED to happen:
For strings to be successfully formatted with the provided formatter to the context

What ACTUALLY happened:
Some sizes where formatted with the formatter of another context.

VERSION of bitmath effected:

  • Version: 1.3.1.1
  • Install Source: PyPi

Your OPERATING SYSTEM and the affected PYTHON VERSION:
unbuntu16.04 and python 2.7.14

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions