[Fedora-suds-list] Multithreading bug in "cloned" clients (0.3.9 and svn-trunk)

Daniel Rodriguez danjrod at gmail.com
Thu Jul 15 13:17:31 UTC 2010


Dear Jeff and other sudders,

I have posted this as ticket 331, but in case anyone is multithreading.

The application I develop on top of suds is multithreaded and was hitting a
small race condition, where suds would return the object corresponding to
the next call.

The suds client "clone" method should ideally return an object that could be
used in a thread-safe manner, but it didn't seem the case.

After discarding my own transport (by switching back to suds original
transport) I think I have nailed it down to the construction of the method
bindings.

In suds/wsdl.py:

    def add_methods(self, service):
        """ Build method view for service """
        bindings = {
*----> Problem is here <--------------*
            'document/literal' : Document(self),
            'rpc/literal' : RPC(self),
            'rpc/encoded' : Encoded(self)
*----> Problem is here <--------------*
        }
        for p in service.ports:
            binding = p.binding
            ptype = p.binding.type
            operations = p.binding.type.operations.values()
            for name in [op.name for op in operations]:
                m = Facade('Method')
                m.name = name
                m.location = p.location
                m.binding = Facade('binding')
                op = binding.operation(name)
                m.soap = op.soap
                key = '/'.join((op.soap.style, op.soap.input.body.use))
                m.binding.input = bindings.get(key) *----> Problem is here
<--------------*
                key = '/'.join((op.soap.style, op.soap.output.body.use))
                m.binding.output = bindings.get(key) *----> Problem is here
<--------------*
                op = ptype.operation(name)
                p.methods[name] = m


The same "binding" (Document in my case) object is being assigned to all
methods and shared by all instances of a suds Client created with clone.
This is apparently why while still processing one method, the next one (in
another thread) would actually overwrite the expected output.

The solution is to delay the binding construction until the binding is
assigned to the method (all still within the same function):

    def add_methods(self, service):
        """ Build method view for service """
        bindings = {
*----> Only reference to the class, no construction <--------------*
            'document/literal' : Document,
            'rpc/literal' : RPC,
            'rpc/encoded' : Encoded
*----> Only reference to the class, no construction <--------------*
        }
        for p in service.ports:
            binding = p.binding
            ptype = p.binding.type
            operations = p.binding.type.operations.values()
            for name in [op.name for op in operations]:
                m = Facade('Method')
                m.name = name
                m.location = p.location
                m.binding = Facade('binding')
                op = binding.operation(name)
                m.soap = op.soap
                key = '/'.join((op.soap.style, op.soap.input.body.use))
                m.binding.input = bindings.get(key)(self) *---->
Construction of the binding object <--------------*
                key = '/'.join((op.soap.style, op.soap.output.body.use))
                m.binding.output = bindings.get(key)(self) *---->
Construction of the binding object <--------------*
                op = ptype.operation(name)
                p.methods[name] = m

As with any multithreading problem, this seems to be a cure for the problem.
The race condition hasn't shown up again, which by no means ensures its
removal, but may have drastically reduced the probability of occurrence.

Best regards

Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.fedoraproject.org/pipermail/suds/attachments/20100715/9cdef13d/attachment.html 


More information about the suds mailing list