Pass receiver to :GC.subscribeDestruction explicitly

Makes the subscriber object be an explicit parameter of
destruction dependency subscribe/unsibscribe method. It makes
possible use of this functions from static methods and to link
other objects.

Targets-blueprint: dependency-driven-resource-deallocation
Change-Id: I2554385bf2ebc6849f09939530a7c090fb745778
This commit is contained in:
Stan Lagun 2016-09-06 23:47:22 -07:00
parent 584571c3d8
commit c47269128b
4 changed files with 76 additions and 67 deletions

View File

@ -12,60 +12,56 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from yaql.language import specs
from yaql.language import yaqltypes
from murano.dsl import dsl
from murano.dsl import helpers
LOG = logging.getLogger(__name__)
@dsl.name('io.murano.system.GC')
class GarbageCollector(object):
@staticmethod
@specs.parameter('target', dsl.MuranoObjectParameter())
@specs.parameter('publisher', dsl.MuranoObjectParameter())
@specs.parameter('subscriber', dsl.MuranoObjectParameter())
@specs.parameter('handler', yaqltypes.String(nullable=True))
def subscribe_destruction(target, handler=None):
caller_context = helpers.get_caller_context()
target_this = target.object.real_this
receiver = helpers.get_this(caller_context)
def subscribe_destruction(publisher, subscriber, handler=None):
publisher_this = publisher.object.real_this
subscriber_this = subscriber.object.real_this
if handler:
receiver.type.find_single_method(handler)
subscriber.type.find_single_method(handler)
dependency = GarbageCollector._find_dependency(
target_this, receiver, handler)
publisher_this, subscriber_this, handler)
if not dependency:
dependency = {'subscriber': helpers.weak_ref(receiver),
dependency = {'subscriber': helpers.weak_ref(subscriber_this),
'handler': handler}
target_this.dependencies.setdefault(
publisher_this.dependencies.setdefault(
'onDestruction', []).append(dependency)
@staticmethod
@specs.parameter('target', dsl.MuranoObjectParameter())
@specs.parameter('publisher', dsl.MuranoObjectParameter())
@specs.parameter('subscriber', dsl.MuranoObjectParameter())
@specs.parameter('handler', yaqltypes.String(nullable=True))
def unsubscribe_destruction(target, handler=None):
caller_context = helpers.get_caller_context()
target_this = target.object.real_this
receiver = helpers.get_this(caller_context)
def unsubscribe_destruction(publisher, subscriber, handler=None):
publisher_this = publisher.object.real_this
subscriber_this = subscriber.object.real_this
if handler:
receiver.type.find_single_method(handler)
subscriber.type.find_single_method(handler)
dds = target_this.dependencies.get('onDestruction', [])
dds = publisher_this.dependencies.get('onDestruction', [])
dependency = GarbageCollector._find_dependency(
target_this, receiver, handler)
publisher_this, subscriber_this, handler)
if dependency:
dds.remove(dependency)
@staticmethod
def _find_dependency(target, subscriber, handler):
dds = target.dependencies.get('onDestruction', [])
def _find_dependency(publisher, subscriber, handler):
dds = publisher.dependencies.get('onDestruction', [])
for dd in dds:
if dd['handler'] != handler:
continue

View File

@ -6,6 +6,17 @@ Methods:
Body:
- $.find(Node)
foo:
Body:
- trace(foo)
destructionHandler:
Arguments:
- obj:
Contract: $.class(TestGCNode).notNull()
Body:
- trace('Destruction of {0}'.format($obj.value))
.destroy:
Body:
- trace($.value)
@ -37,8 +48,9 @@ Methods:
:TestGCNode:
value: B
- $x: new($model)
- sys:GC.subscribeDestruction($x, _handler)
- sys:GC.subscribeDestruction($x.nodes[0], _handler)
- sys:GC.subscribeDestruction($x, $this, _handler)
- sys:GC.subscribeDestruction($x.nodes[0], $this, _handler)
- sys:GC.subscribeDestruction($x.nodes[0], $x, destructionHandler)
- $x: null
- sys:GC.collect()

View File

@ -53,4 +53,6 @@ class TestGC(test_case.DslTestCase):
def test_collect_with_subscription(self):
self.runner.testObjectsCollectWithSubscription()
self.assertEqual(['Destroy A', 'Destroy B', 'B', 'A'], self.traces)
self.assertEqual(
['Destroy A', 'Destroy B', 'Destruction of B', 'B', 'A'],
self.traces)

View File

@ -12,11 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import _weakref
import weakref
import mock
from testtools import matchers
import unittest
from murano.dsl import dsl
from murano.dsl import exceptions
@ -31,57 +30,57 @@ class TestGarbageCollector(base.MuranoTestCase):
def setUp(self):
super(TestGarbageCollector, self).setUp()
self.mock_subscriber = mock.MagicMock(spec=murano_object.MuranoObject)
self.mock_class = mock.MagicMock(spec=murano_type.MuranoClass)
self.mock_method = mock.MagicMock(spec=murano_method.MuranoMethod)
self.mock_method.name = "mockHandler"
self.mock_class.methods = mock.PropertyMock(
return_value={"mockHandler": self.mock_method})
self.mock_subscriber.type = self.mock_class
self.mock_subscriber.object_id = "1234"
subscriber = mock.MagicMock(spec=murano_object.MuranoObject)
subscriber.real_this = subscriber
@mock.patch("murano.dsl.helpers.get_caller_context")
@mock.patch("murano.dsl.helpers.get_this")
def test_set_dd(self, this, caller_context):
this.return_value = self.mock_subscriber
target_0 = mock.MagicMock(spec=dsl.MuranoObjectInterface)
target_0.object = self.mock_subscriber
target_0.object.real_this = self.mock_subscriber
target_0.object.dependencies = {}
mock_class = mock.MagicMock(spec=murano_type.MuranoClass)
mock_method = mock.MagicMock(spec=murano_method.MuranoMethod)
mock_method.name = "mockHandler"
mock_class.methods = mock.PropertyMock(
return_value={"mockHandler": mock_method})
def find_single_method(name):
if name != 'mockHandler':
raise exceptions.NoMethodFound(name)
mock_class.find_single_method = find_single_method
subscriber.type = mock_class
self.subscriber = mock.MagicMock(spec=dsl.MuranoObjectInterface)
self.subscriber.object = subscriber
self.subscriber.type = subscriber.type
publisher = mock.MagicMock(spec=murano_object.MuranoObject)
publisher.real_this = publisher
self.publisher = mock.MagicMock(spec=dsl.MuranoObjectInterface)
self.publisher.object = publisher
def test_set_dd(self):
self.publisher.object.dependencies = {}
garbage_collector.GarbageCollector.subscribe_destruction(
target_0, handler="mockHandler")
dep = self.mock_subscriber.dependencies["onDestruction"]
self.publisher, self.subscriber, handler="mockHandler")
dep = self.publisher.object.dependencies["onDestruction"]
self.assertThat(dep, matchers.HasLength(1))
dep = dep[0]
self.assertEqual("mockHandler", dep["handler"])
self.assertEqual(self.mock_subscriber, dep["subscriber"]())
self.assertEqual(self.subscriber.object, dep["subscriber"]())
@mock.patch("murano.dsl.helpers.get_caller_context")
@mock.patch("murano.dsl.helpers.get_this")
def test_unset_dd(self, this, caller_context):
this.return_value = self.mock_subscriber
target_1 = mock.MagicMock(spec=dsl.MuranoObjectInterface)
target_1.object = self.mock_subscriber
target_1.object.real_this = self.mock_subscriber
target_1.object.dependencies = (
def test_unset_dd(self):
self.publisher.object.dependencies = (
{"onDestruction": [{
"subscriber": _weakref.ref(self.mock_subscriber),
"subscriber": weakref.ref(self.subscriber.object),
"handler": "mockHandler"
}]})
garbage_collector.GarbageCollector.unsubscribe_destruction(
target_1, handler="mockHandler")
self.publisher, self.subscriber, handler="mockHandler")
self.assertEqual(
[], self.mock_subscriber.dependencies["onDestruction"])
[], self.publisher.object.dependencies["onDestruction"])
@unittest.skip("WIP")
@mock.patch("murano.dsl.helpers.get_caller_context")
@mock.patch("murano.dsl.helpers.get_this")
def test_set_wrong_handler(self, this, caller_context):
this.return_value = self.mock_subscriber
target_2 = mock.MagicMock(spec=dsl.MuranoObjectInterface)
target_2.object = mock.MagicMock(murano_object.MuranoObject)
target_2.object.dependencies = {}
def test_set_wrong_handler(self):
self.assertRaises(
exceptions.NoMethodFound,
garbage_collector.GarbageCollector.subscribe_destruction,
target_2, handler="wrongHandler")
self.publisher, self.subscriber, handler="invalidHandler")
self.assertRaises(
exceptions.NoMethodFound,
garbage_collector.GarbageCollector.unsubscribe_destruction,
self.publisher, self.subscriber, handler="invalidHandler")