From 2af6e92b78944879fbb41fd4538be15b97402f88 Mon Sep 17 00:00:00 2001 From: Paul Vinciguerra Date: Thu, 6 Jun 2019 07:06:09 -0400 Subject: [PATCH 1/1] vpp_papi: Context_id allocator for running forked. When running forked, distinct copies of the 'get_context' singleton are created for each process. To run under forked processes, (as with make test TEST_JOBS=10), we need to use a shared memory value across the processes. Type: fix Change-Id: I9eab8ce46ec23584e5bd651735ad75fd3f018e1a Signed-off-by: Paul Vinciguerra --- src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py | 53 ++++++++++++++++++++++ src/vpp-api/python/vpp_papi/vpp_papi.py | 12 +++-- 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py new file mode 100644 index 00000000000..774b4e1092c --- /dev/null +++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_papi.py @@ -0,0 +1,53 @@ +# Copyright (c) 2019. Vinci Consulting Corp. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import ctypes +import multiprocessing as mp +import unittest +from vpp_papi import vpp_papi + + +class TestVppPapiVPPApiClient(unittest.TestCase): + + def test_getcontext(self): + vpp_papi.VPPApiClient.apidir = '.' + c = vpp_papi.VPPApiClient(testmode=True, use_socket=True) + + # reset initialization at module load time. + c.get_context.context = mp.Value(ctypes.c_uint, 0) + for _ in range(10): + c.get_context() + self.assertEqual(11, c.get_context()) + + +class TestVppPapiVPPApiClientMp(unittest.TestCase): + # Test under multiple processes to simulate running forked under + # run_tests.py (eg. make test TEST_JOBS=10) + + def test_get_context_mp(self): + vpp_papi.VPPApiClient.apidir = '.' + c = vpp_papi.VPPApiClient(testmode=True, use_socket=True) + + # reset initialization at module load time. + c.get_context.context = mp.Value(ctypes.c_uint, 0) + procs = [mp.Process(target=c.get_context, args=()) for i in range(10)] + + for p in procs: + p.start() + for p in procs: + p.join() + + # AssertionError: 11 != 1 + self.assertEqual(11, c.get_context()) + diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py index 8f179a21a9d..1f5cce23a79 100644 --- a/src/vpp-api/python/vpp_papi/vpp_papi.py +++ b/src/vpp-api/python/vpp_papi/vpp_papi.py @@ -16,7 +16,9 @@ from __future__ import print_function from __future__ import absolute_import +import ctypes import sys +import multiprocessing as mp import os import logging import collections @@ -263,16 +265,16 @@ class VPPApiClient(object): atexit.register(vpp_atexit, weakref.ref(self)) class ContextId(object): - """Thread-safe provider of unique context IDs.""" + """Multiprocessing-safe provider of unique context IDs.""" def __init__(self): - self.context = 0 - self.lock = threading.Lock() + self.context = mp.Value(ctypes.c_uint, 0) + self.lock = mp.Lock() def __call__(self): """Get a new unique (or, at least, not recently used) context.""" with self.lock: - self.context += 1 - return self.context + self.context.value += 1 + return self.context.value get_context = ContextId() def get_type(self, name): -- 2.16.6