summaryrefslogtreecommitdiffstats
path: root/python/lock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'python/lock.cc')
-rw-r--r--python/lock.cc268
1 files changed, 268 insertions, 0 deletions
diff --git a/python/lock.cc b/python/lock.cc
new file mode 100644
index 0000000..009605e
--- /dev/null
+++ b/python/lock.cc
@@ -0,0 +1,268 @@
+/*
+ * lock.cc - Context managers for implementing locking.
+ *
+ * Copyright 2009 Julian Andres Klode <jak@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include <Python.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/pkgsystem.h>
+#include "generic.h"
+
+static PyObject *systemlock_exit(PyObject *self, PyObject *args)
+{
+
+ PyObject *exc_type = 0;
+ PyObject *exc_value = 0;
+ PyObject *traceback = 0;
+ if (!PyArg_UnpackTuple(args, "__exit__", 3, 3, &exc_type, &exc_value,
+ &traceback)) {
+ return 0;
+ }
+
+ if (_system->UnLock() == 0) {
+ // The unlock failed. If no exception happened within the suite, we
+ // will raise an error here. Otherwise, we just display the error, so
+ // Python can handle the original exception instead.
+ HandleErrors();
+ if (exc_type == Py_None)
+ return NULL;
+ else
+ PyErr_WriteUnraisable(self);
+ }
+ // Return False, as required by the context manager protocol.
+ Py_RETURN_FALSE;
+}
+
+static PyObject *systemlock_enter(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ if (!_system->Lock())
+ return HandleErrors();
+ Py_INCREF(self);
+ return self;
+}
+
+static PyObject *systemlock_new(PyTypeObject *type, PyObject *args,
+ PyObject *kwds)
+{
+ if (_system == 0) {
+ PyErr_SetString(PyExc_ValueError,"_system not initialized");
+ return 0;
+ }
+ return PyType_GenericNew(type,args,kwds);
+}
+
+static PyMethodDef systemlock_methods[] = {
+ {"__enter__",systemlock_enter,METH_VARARGS,"Lock the system."},
+ {"__exit__",systemlock_exit,METH_VARARGS,"Unlock the system."},
+ {NULL}
+};
+
+static char *systemlock_doc = "SystemLock()\n\n"
+ "Context manager for locking the package system. The lock is established\n"
+ "as soon as the method __enter__() is called. It is released when\n"
+ "__exit__() is called.\n\n"
+ "This should be used via the 'with' statement, for example:\n\n"
+ " with apt_pkg.SystemLock():\n"
+ " ...\n\n"
+ "Once the block is left, the lock is released automatically. The object\n"
+ "can be used multiple times:\n\n"
+ " lock = apt_pkg.SystemLock()\n"
+ " with lock:\n"
+ " ...\n"
+ " with lock:\n"
+ " ...\n\n";
+
+PyTypeObject PySystemLock_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "apt_pkg.SystemLock", // tp_name
+ 0, // tp_basicsize
+ 0, // tp_itemsize
+ // Methods
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ (Py_TPFLAGS_DEFAULT | // tp_flags
+ Py_TPFLAGS_BASETYPE),
+ systemlock_doc, // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ systemlock_methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ systemlock_new, // tp_new
+};
+
+
+/**
+ * File Based locking.
+ *
+ * The counter is increased by every call to filelock_enter() and decreased by
+ * every call to filelock_exit(). When the counter reaches 0, the underlying
+ * file descriptor is closed.
+ *
+ * Members:
+ * @member char* filename The name of the file
+ * @member int lock_count How many times we have locked it.
+ * @member int fd The filedescriptor returned by GetLock() or 0.
+ */
+struct filelock_object {
+ PyObject_HEAD
+ char *filename;
+ int lock_count;
+ int fd;
+};
+
+static PyObject *filelock_enter(filelock_object *self, PyObject *args)
+{
+ self->lock_count++;
+ // If we have no lock yet, get a lock.
+ if (self->lock_count == 1) {
+ self->fd = GetLock(self->filename, true);
+ if (self->fd == -1) {
+ self->lock_count--;
+ return HandleErrors();
+ }
+ }
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+static PyObject *filelock_exit(filelock_object *self, PyObject *args)
+{
+ // Count down the lock_count, if it is less than 0, reset it to 0.
+ self->lock_count--;
+ if (self->lock_count < 0)
+ self->lock_count = 0;
+ if (self->lock_count == 0 && self->fd != 0 && close(self->fd) == -1) {
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
+ Py_RETURN_FALSE;
+}
+
+static PyObject *filelock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyApt_Filename filename;
+ char *kwlist[] = {"filename", NULL};
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O&:__init__", kwlist,
+ PyApt_Filename::Converter,
+ &filename) == 0) {
+ return NULL;
+ }
+ filelock_object *self = (filelock_object *)type->tp_alloc(type, 0);
+ // Copy the string into the object.
+ self->filename = new char[strlen(filename) + 1];
+ strcpy(self->filename, filename);
+ return (PyObject *)self;
+}
+
+static void filelock_dealloc(filelock_object *self)
+{
+ delete[] self->filename;
+ ((PyObject*)self)->ob_type->tp_free(self);
+}
+
+static PyMethodDef filelock_methods[] = {
+ {"__enter__",(PyCFunction)filelock_enter,METH_VARARGS,"Lock the system."},
+ {"__exit__",(PyCFunction)filelock_exit,METH_VARARGS,"Unlock the system."},
+ {NULL}
+};
+
+static char *filelock_doc = "FileLock(filename: str)\n\n"
+ "Context manager for locking using a file. The lock is established\n"
+ "as soon as the method __enter__() is called. It is released when\n"
+ "__exit__() is called.\n\n"
+ "This should be used via the 'with' statement, for example:\n\n"
+ " with apt_pkg.FileLock(filename):\n"
+ " ...\n\n"
+ "Once the block is left, the lock is released automatically. The object\n"
+ "can be used multiple times:\n\n"
+ " lock = apt_pkg.FileLock(filename)\n"
+ " with lock:\n"
+ " ...\n"
+ " with lock:\n"
+ " ...\n\n";
+
+PyTypeObject PyFileLock_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "apt_pkg.FileLock", // tp_name
+ sizeof(filelock_object), // tp_basicsize
+ 0, // tp_itemsize
+ // Methods
+ destructor(filelock_dealloc), // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ (Py_TPFLAGS_DEFAULT | // tp_flags
+ Py_TPFLAGS_BASETYPE),
+ filelock_doc, // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ filelock_methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ filelock_new, // tp_new
+};