Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

edgify / persistent   python

Repository URL to install this package:

/ cPersistence.c

/*****************************************************************************

  Copyright (c) 2001, 2002 Zope Foundation and Contributors.
  All Rights Reserved.

  This software is subject to the provisions of the Zope Public License,
  Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  FOR A PARTICULAR PURPOSE

****************************************************************************/
static char cPersistence_doc_string[] =
  "Defines Persistent mixin class for persistent objects.\n"
  "\n"
  "$Id$\n";

#define PY_SSIZE_T_CLEAN
#include "cPersistence.h"
#include "structmember.h"

struct ccobject_head_struct
{
    CACHE_HEAD
};

/*
  The compiler on Windows used for Python 2.7 doesn't include
  stdint.h.
*/
#if !defined(PY3K) && defined(_WIN32)
typedef unsigned long long uint64_t;
#  define PRIx64 "llx"
#else
#  include <stdint.h>
#  include <inttypes.h>
#endif

/* These objects are initialized when the module is loaded */
static PyObject *py_simple_new;

/* Strings initialized by init_strings() below. */
static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
static PyObject *py__p_changed, *py__p_deactivate;
static PyObject *py___getattr__, *py___setattr__, *py___delattr__;
static PyObject *py___slotnames__, *copy_reg_slotnames, *__newobj__;
static PyObject *py___getnewargs__, *py___getstate__;
static PyObject *py_unsaved, *py_ghost, *py_saved, *py_changed, *py_sticky;


static int
init_strings(void)
{
#define INIT_STRING(S)                              \
  if (!(py_ ## S = INTERN(#S)))  \
    return -1;
  INIT_STRING(keys);
  INIT_STRING(setstate);
  INIT_STRING(timeTime);
  INIT_STRING(__dict__);
  INIT_STRING(_p_changed);
  INIT_STRING(_p_deactivate);
  INIT_STRING(__getattr__);
  INIT_STRING(__setattr__);
  INIT_STRING(__delattr__);
  INIT_STRING(__slotnames__);
  INIT_STRING(__getnewargs__);
  INIT_STRING(__getstate__);
  INIT_STRING(unsaved);
  INIT_STRING(ghost);
  INIT_STRING(saved);
  INIT_STRING(changed);
  INIT_STRING(sticky);
#undef INIT_STRING
  return 0;
}

#ifdef Py_DEBUG
static void
fatal_1350(cPersistentObject *self, const char *caller, const char *detail)
{
    char buf[1000];

    PyOS_snprintf(buf, sizeof(buf),
                "cPersistence.c %s(): object at %p with type %.200s\n"
                "%s.\n"
                "The only known cause is multiple threads trying to ghost and\n"
                "unghost the object simultaneously.\n"
                "That's not legal, but ZODB can't stop it.\n"
                "See Collector #1350.\n",
                caller, self, Py_TYPE(self)->tp_name, detail);
    Py_FatalError(buf);
}
#endif

static void ghostify(cPersistentObject*);
static PyObject * pickle_slotnames(PyTypeObject *cls);

static PyObject * convert_name(PyObject *name);

/* Load the state of the object, unghostifying it.  Upon success, return 1.
 * If an error occurred, re-ghostify the object and return -1.
 */
static int
unghostify(cPersistentObject *self)
{
    if (self->state < 0 && self->jar)
    {
        PyObject *r;

        /* Is it ever possible to not have a cache? */
        if (self->cache)
        {
            /* Create a node in the ring for this unghostified object. */
            self->cache->non_ghost_count++;
            self->cache->total_estimated_size +=
                _estimated_size_in_bytes(self->estimated_size);
            ring_add(&self->cache->ring_home, &self->ring);
            Py_INCREF(self);
        }
        /* set state to CHANGED while setstate() call is in progress
            to prevent a recursive call to _PyPersist_Load().
        */
        self->state = cPersistent_CHANGED_STATE;
        /* Call the object's __setstate__() */
        r = PyObject_CallMethod(self->jar, "setstate", "O", (PyObject *)self);
        if (r == NULL)
        {
            ghostify(self);
            return -1;
        }
        self->state = cPersistent_UPTODATE_STATE;
        Py_DECREF(r);
        if (self->cache && self->ring.r_next == NULL)
        {
#ifdef Py_DEBUG
            fatal_1350(self, "unghostify",
                     "is not in the cache despite that we just "
                     "unghostified it");
#else
            PyErr_Format(PyExc_SystemError, "object at %p with type "
                       "%.200s not in the cache despite that we just "
                       "unghostified it", self, Py_TYPE(self)->tp_name);
            return -1;
#endif
        }
    }
    return 1;
}

/****************************************************************************/

static PyTypeObject Pertype;

static void
accessed(cPersistentObject *self)
{
    /* Do nothing unless the object is in a cache and not a ghost. */
    if (self->cache && self->state >= 0 && self->ring.r_next)
        ring_move_to_head(&self->cache->ring_home, &self->ring);
}

static void
ghostify(cPersistentObject *self)
{
    PyObject **dictptr, *slotnames;
    PyObject *errtype, *errvalue, *errtb;

    /* are we already a ghost? */
    if (self->state == cPersistent_GHOST_STATE)
        return;

    /* Is it ever possible to not have a cache? */
    if (self->cache == NULL)
    {
        self->state = cPersistent_GHOST_STATE;
        return;
    }

    if (self->ring.r_next == NULL)
    {
        /* There's no way to raise an error in this routine. */
#ifdef Py_DEBUG
        fatal_1350(self, "ghostify", "claims to be in a cache but isn't");
#else
        return;
#endif
    }

    /* If we're ghostifying an object, we better have some non-ghosts. */
    assert(self->cache->non_ghost_count > 0);
    self->cache->non_ghost_count--;
    self->cache->total_estimated_size -=
        _estimated_size_in_bytes(self->estimated_size);
    ring_del(&self->ring);
    self->state = cPersistent_GHOST_STATE;

    /* clear __dict__ */
    dictptr = _PyObject_GetDictPtr((PyObject *)self);
    if (dictptr && *dictptr)
    {
        Py_DECREF(*dictptr);
        *dictptr = NULL;
    }

    /* clear all slots besides _p_*
     * ( for backward-compatibility reason we do this only if class does not
     *   override __new__ ) */
    if (Py_TYPE(self)->tp_new == Pertype.tp_new)
    {
        /* later we might clear an AttributeError but
         * if we have a pending exception that still needs to be
         * raised so that we don't generate a SystemError.
         */
        PyErr_Fetch(&errtype, &errvalue, &errtb);

        slotnames = pickle_slotnames(Py_TYPE(self));
        if (slotnames && slotnames != Py_None)
        {
            int i;

            for (i = 0; i < PyList_GET_SIZE(slotnames); i++)
            {
                PyObject *name;
                char *cname;
                int is_special;

                name = PyList_GET_ITEM(slotnames, i);
#ifdef PY3K
                if (PyUnicode_Check(name))
                {
                    PyObject *converted = convert_name(name);
                    cname = PyBytes_AS_STRING(converted);
#else
                if (PyBytes_Check(name))
                {
                    cname = PyBytes_AS_STRING(name);
#endif
                    is_special = !strncmp(cname, "_p_", 3);
#ifdef PY3K
                    Py_DECREF(converted);
#endif
                    if (is_special) /* skip persistent */
                    {
                        continue;
                    }
                }

                /* NOTE: this skips our delattr hook */
                if (PyObject_GenericSetAttr((PyObject *)self, name, NULL) < 0)
                    /* delattr of non-set slot will raise AttributeError - we
                     * simply ignore. */
                    PyErr_Clear();
            }
        }
        Py_XDECREF(slotnames);
        PyErr_Restore(errtype, errvalue, errtb);
    }

    /* We remove the reference to the just ghosted object that the ring
    * holds.  Note that the dictionary of oids->objects has an uncounted
    * reference, so if the ring's reference was the only one, this frees
    * the ghost object.  Note further that the object's dealloc knows to
    * inform the dictionary that it is going away.
    */
    Py_DECREF(self);
}

static int
changed(cPersistentObject *self)
{
    if ((self->state == cPersistent_UPTODATE_STATE ||
        self->state == cPersistent_STICKY_STATE)
        && self->jar)
    {
        PyObject *meth, *arg, *result;
        static PyObject *s_register;

        if (s_register == NULL)
            s_register = INTERN("register");
        meth = PyObject_GetAttr((PyObject *)self->jar, s_register);
        if (meth == NULL)
            return -1;
        arg = PyTuple_New(1);
        if (arg == NULL)
        {
            Py_DECREF(meth);
            return -1;
        }
        Py_INCREF(self);
        PyTuple_SET_ITEM(arg, 0, (PyObject *)self);
        result = PyObject_CallObject(meth, arg);
        Py_DECREF(arg);
        Py_DECREF(meth);
        if (result == NULL)
            return -1;
        Py_DECREF(result);

        self->state = cPersistent_CHANGED_STATE;
    }

    return 0;
}

static int
readCurrent(cPersistentObject *self)
{
    if ((self->state == cPersistent_UPTODATE_STATE ||
        self->state == cPersistent_STICKY_STATE)
        && self->jar && self->oid)
    {
        static PyObject *s_readCurrent=NULL;
        PyObject *r;

        if (s_readCurrent == NULL)
            s_readCurrent = INTERN("readCurrent");

        r = PyObject_CallMethodObjArgs(self->jar, s_readCurrent, self, NULL);
        if (r == NULL)
            return -1;

        Py_DECREF(r);
    }

    return 0;
}

static PyObject *
Per__p_deactivate(cPersistentObject *self)
{
    if (self->state == cPersistent_UPTODATE_STATE && self->jar)
    {
        PyObject **dictptr = _PyObject_GetDictPtr((PyObject *)self);
        if (dictptr && *dictptr)
        {
            Py_DECREF(*dictptr);
            *dictptr = NULL;
        }
        /* Note that we need to set to ghost state unless we are
            called directly. Methods that override this need to
            do the same! */
        ghostify(self);
        if (PyErr_Occurred())
            return NULL;
Loading ...