Commit 6915fe26 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

utils.cfunc: Cleanup, more flexibility

- Split code using ctypes directly into a helper class
- Don't load “”, but use handle for main program instead (see
  comment in code)
- Clarify comment on errno with older ctypes versions
- Rename unittest since it can't be used for other functions (modifies
  process environment at runtime)
- Add boolean return value for “Mlockall”

These changes are leftovers from some experiments with ctypes.
Signed-off-by: default avatarMichael Hanselmann <>
Reviewed-by: default avatarIustin Pop <>
parent 57ca011e
......@@ -724,7 +724,7 @@ python_tests = \
test/ \
test/ \
test/ \
test/ \
test/ \
test/ \
test/ \
test/ \
......@@ -34,44 +34,61 @@ except ImportError:
ctypes = None
# Flags for mlockall() (from bits/mman.h)
# Flags for mlockall(2) (from bits/mman.h)
class _FuncWrapper:
def __init__(self, ct):
"""Initializes this class.
# Make use of a dlopen(3) feature whereby giving a NULL handle returns the
# main program. Functions from all previously loaded libraries can then be
# used.
mainprog = ct.CDLL(None)
# The ctypes module before Python 2.6 does not have built-in functionality
# to access the global errno global (which, depending on the libc and build
# options, is per-thread), where function error codes are stored. Use GNU
# libc's way to retrieve errno(3) instead.
errno_loc = getattr(mainprog, "__errno_location")
except AttributeError, err:
logging.debug("Unable to find errno location: %s", err)
errno_fn = None
errno_loc.restype = ct.POINTER(ct.c_int)
errno_fn = lambda: errno_loc().contents.value
self.errno_fn = errno_fn
# Try to get mlockall(2)
self.mlockall_fn = getattr(mainprog, "mlockall", None)
def Mlockall(_ctypes=ctypes):
"""Lock current process' virtual address space into RAM.
This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
see mlock(2) for more details. This function requires ctypes module.
This is equivalent to the C call C{mlockall(MCL_CURRENT | MCL_FUTURE)}. See
mlockall(2) for more details. This function requires the C{ctypes} module.
@raises errors.NoCtypesError: if ctypes module is not found
@raises errors.NoCtypesError: If the C{ctypes} module is not found
if _ctypes is None:
raise errors.NoCtypesError()
libc = _ctypes.cdll.LoadLibrary("")
except EnvironmentError, err:
logging.error("Failure trying to load libc: %s", err)
libc = None
if libc is None:
logging.error("Cannot set memory lock, ctypes cannot load libc")
# Some older version of the ctypes module don't have built-in functionality
# to access the errno global variable, where function error codes are stored.
# By declaring this variable as a pointer to an integer we can then access
# its value correctly, should the mlockall call fail, in order to see what
# the actual error code was.
# pylint: disable=W0212
libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
# pylint: disable=W0212
logging.error("Cannot set memory lock: %s",
logging.debug("Memory lock set")
funcs = _FuncWrapper(_ctypes)
if funcs.mlockall_fn is None:
logging.debug("libc doesn't support mlockall(2)")
if funcs.mlockall_fn(_MCL_CURRENT | _MCL_FUTURE) == 0:
logging.debug("Memory lock set")
return True
logging.error("Cannot set memory lock: %s", os.strerror(funcs.errno_fn()))
return False
......@@ -41,17 +41,15 @@ class TestMlockallWithCtypes(unittest.TestCase):
"""Whether Mlockall() works if ctypes is present.
def test(self):
if utils.ctypes:
class TestMlockallWithNoCtypes(unittest.TestCase):
"""Whether Mlockall() raises an error if ctypes is not present.
def test(self):
self.assertRaises(errors.NoCtypesError, utils.Mlockall, _ctypes=None)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment