xref: /aoo42x/main/binaryurp/source/bridge.cxx (revision 37adc4f0)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "sal/config.h"
25 
26 #include <algorithm>
27 #include <cstddef>
28 #include <limits>
29 #include <memory>
30 #include <vector>
31 
32 #include "boost/noncopyable.hpp"
33 #include "com/sun/star/bridge/InvalidProtocolChangeException.hpp"
34 #include "com/sun/star/bridge/XBridge.hpp"
35 #include "com/sun/star/bridge/XInstanceProvider.hpp"
36 #include "com/sun/star/bridge/XProtocolProperties.hpp"
37 #include "com/sun/star/connection/XConnection.hpp"
38 #include "com/sun/star/io/IOException.hpp"
39 #include "com/sun/star/lang/DisposedException.hpp"
40 #include "com/sun/star/lang/EventObject.hpp"
41 #include "com/sun/star/lang/XEventListener.hpp"
42 #include "com/sun/star/uno/Reference.hxx"
43 #include "com/sun/star/uno/RuntimeException.hpp"
44 #include "com/sun/star/uno/Sequence.hxx"
45 #include "com/sun/star/uno/XInterface.hpp"
46 #include "cppuhelper/exc_hlp.hxx"
47 #include "cppuhelper/weak.hxx"
48 #include "osl/diagnose.h"
49 #include "osl/mutex.hxx"
50 #include "osl/thread.hxx"
51 #include "rtl/byteseq.hxx"
52 #include "rtl/random.h"
53 #include "rtl/ref.hxx"
54 #include "rtl/textenc.h"
55 #include "rtl/ustrbuf.hxx"
56 #include "rtl/ustring.h"
57 #include "rtl/ustring.hxx"
58 #include "sal/types.h"
59 #include "typelib/typeclass.h"
60 #include "typelib/typedescription.h"
61 #include "typelib/typedescription.hxx"
62 #include "uno/dispatcher.hxx"
63 #include "uno/environment.hxx"
64 #include "uno/lbnames.h"
65 
66 #include "binaryany.hxx"
67 #include "bridge.hxx"
68 #include "bridgefactory.hxx"
69 #include "incomingreply.hxx"
70 #include "lessoperators.hxx"
71 #include "outgoingrequest.hxx"
72 #include "outgoingrequests.hxx"
73 #include "proxy.hxx"
74 #include "reader.hxx"
75 
76 namespace binaryurp {
77 
78 namespace {
79 
80 namespace css = com::sun::star;
81 
82 sal_Int32 random() {
83     sal_Int32 n;
84     rtlRandomPool pool = rtl_random_createPool();
85     rtl_random_getBytes(pool, &n, sizeof n);
86     rtl_random_destroyPool(pool);
87     return n;
88 }
89 
90 extern "C" void SAL_CALL freeProxyCallback(uno_ExtEnvironment *, void * pProxy)
91 {
92     OSL_ASSERT(pProxy != 0);
93     static_cast< Proxy * >(pProxy)->do_free();
94 }
95 
96 void joinThread(osl::Thread * thread) {
97     OSL_ASSERT(thread != 0);
98     if (thread->getIdentifier() != osl::Thread::getCurrentIdentifier()) {
99         thread->join();
100     }
101 }
102 
103 class AttachThread: private boost::noncopyable {
104 public:
105     explicit AttachThread(uno_ThreadPool threadPool);
106 
107     ~AttachThread();
108 
109     rtl::ByteSequence getTid() throw ();
110 
111 private:
112     uno_ThreadPool threadPool_;
113     rtl::ByteSequence tid_;
114 };
115 
116 AttachThread::AttachThread(uno_ThreadPool threadPool): threadPool_(threadPool) {
117     sal_Sequence * s = 0;
118     uno_getIdOfCurrentThread(&s);
119     tid_ = rtl::ByteSequence(s, rtl::BYTESEQ_NOACQUIRE);
120     uno_threadpool_attach(threadPool_);
121 }
122 
123 AttachThread::~AttachThread() {
124     uno_threadpool_detach(threadPool_);
125     uno_releaseIdFromCurrentThread();
126 }
127 
128 rtl::ByteSequence AttachThread::getTid() throw () {
129     return tid_;
130 }
131 
132 class PopOutgoingRequest: private boost::noncopyable {
133 public:
134     PopOutgoingRequest(
135         OutgoingRequests & requests, rtl::ByteSequence const & tid,
136         OutgoingRequest const & request);
137 
138     ~PopOutgoingRequest();
139 
140     void clear();
141 
142 private:
143     OutgoingRequests & requests_;
144     rtl::ByteSequence tid_;
145     bool cleared_;
146 };
147 
148 PopOutgoingRequest::PopOutgoingRequest(
149     OutgoingRequests & requests, rtl::ByteSequence const & tid,
150     OutgoingRequest const & request):
151     requests_(requests), tid_(tid), cleared_(false)
152 {
153     requests_.push(tid_, request);
154 }
155 
156 PopOutgoingRequest::~PopOutgoingRequest() {
157     if (!cleared_) {
158         requests_.pop(tid_);
159     }
160 }
161 
162 void PopOutgoingRequest::clear() {
163     cleared_ = true;
164 }
165 
166 }
167 
168 struct Bridge::SubStub {
169     com::sun::star::uno::UnoInterfaceReference object;
170 
171     sal_uInt32 references;
172 };
173 
174 Bridge::Bridge(
175     rtl::Reference< BridgeFactory > const & factory, rtl::OUString const & name,
176     css::uno::Reference< css::connection::XConnection > const & connection,
177     css::uno::Reference< css::bridge::XInstanceProvider > const & provider):
178     factory_(factory), name_(name), connection_(connection),
179     provider_(provider),
180     binaryUno_(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO))),
181     cppToBinaryMapping_(
182         rtl::OUString(
183             RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME)),
184         rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO))),
185     binaryToCppMapping_(
186         rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO)),
187         rtl::OUString(
188             RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME))),
189     protPropTid_(
190         reinterpret_cast< sal_Int8 const * >(".UrpProtocolPropertiesTid"),
191         RTL_CONSTASCII_LENGTH(".UrpProtocolPropertiesTid")),
192     protPropOid_(RTL_CONSTASCII_USTRINGPARAM("UrpProtocolProperties")),
193     protPropType_(
194         cppu::UnoType<
195             css::uno::Reference< css::bridge::XProtocolProperties > >::get()),
196     protPropRequest_(
197         rtl::OUString(
198             RTL_CONSTASCII_USTRINGPARAM(
199                 "com.sun.star.bridge.XProtocolProperties::requestChange"))),
200     protPropCommit_(
201         rtl::OUString(
202             RTL_CONSTASCII_USTRINGPARAM(
203                 "com.sun.star.bridge.XProtocolProperties::commitChange"))),
204     threadPool_(0), currentContextMode_(false), proxies_(0), calls_(0),
205     normalCall_(false), activeCalls_(0), terminated_(false),
206     mode_(MODE_REQUESTED)
207 {
208     OSL_ASSERT(factory.is() && connection.is());
209     if (!binaryUno_.is()) {
210         throw css::uno::RuntimeException(
211             rtl::OUString(
212                 RTL_CONSTASCII_USTRINGPARAM("URP: no binary UNO environment")),
213             css::uno::Reference< css::uno::XInterface >());
214     }
215     if (!(cppToBinaryMapping_.is() && binaryToCppMapping_.is())) {
216         throw css::uno::RuntimeException(
217             rtl::OUString(
218                 RTL_CONSTASCII_USTRINGPARAM("URP: no C++ UNO mapping")),
219             css::uno::Reference< css::uno::XInterface >());
220     }
221     passive_.set();
222 }
223 
224 void Bridge::start() {
225     OSL_ASSERT(threadPool_ == 0 && !writer_.is() && !reader_.is());
226     threadPool_ = uno_threadpool_create();
227     OSL_ASSERT(threadPool_ != 0);
228     writer_.set(new Writer(this));
229     writer_->create();
230     reader_.set(new Reader(this));
231     reader_->create();
232 }
233 
234 void Bridge::terminate() {
235     rtl::Reference< Reader > r;
236     rtl::Reference< Writer > w;
237     Listeners ls;
238     {
239         osl::MutexGuard g(mutex_);
240         if (terminated_) {
241             return;
242         }
243         std::swap(reader_, r);
244         std::swap(writer_, w);
245         ls.swap(listeners_);
246         terminated_ = true;
247     }
248     try {
249         connection_->close();
250     } catch (css::io::IOException & e) {
251         OSL_TRACE(
252             OSL_LOG_PREFIX "caught IO exception '%s'",
253             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
254     }
255     OSL_ASSERT(w.is());
256     w->stop();
257     joinThread(r.get());
258     joinThread(w.get());
259     OSL_ASSERT(threadPool_ != 0);
260     uno_threadpool_dispose(threadPool_);
261     Stubs s;
262     {
263         osl::MutexGuard g(mutex_);
264         s.swap(stubs_);
265     }
266     for (Stubs::iterator i(s.begin()); i != s.end(); ++i) {
267         for (Stub::iterator j(i->second.begin()); j != i->second.end(); ++j) {
268             binaryUno_.get()->pExtEnv->revokeInterface(
269                 binaryUno_.get()->pExtEnv, j->second.object.get());
270         }
271     }
272     factory_->removeBridge(this);
273     for (Listeners::iterator i(ls.begin()); i != ls.end(); ++i) {
274         try {
275             (*i)->disposing(
276                 css::lang::EventObject(
277                     static_cast< cppu::OWeakObject * >(this)));
278         } catch (css::uno::RuntimeException & e) {
279             OSL_TRACE(
280                 OSL_LOG_PREFIX "caught runtime exception '%s'",
281                 rtl::OUStringToOString(
282                     e.Message, RTL_TEXTENCODING_UTF8).getStr());
283         }
284     }
285 }
286 
287 css::uno::Reference< css::connection::XConnection > Bridge::getConnection()
288     const
289 {
290     return connection_;
291 }
292 
293 css::uno::Reference< css::bridge::XInstanceProvider > Bridge::getProvider()
294     const
295 {
296     return provider_;
297 }
298 
299 css::uno::Mapping & Bridge::getCppToBinaryMapping() {
300     return cppToBinaryMapping_;
301 }
302 
303 BinaryAny Bridge::mapCppToBinaryAny(css::uno::Any const & cppAny) {
304     css::uno::Any in(cppAny);
305     BinaryAny out;
306     out.~BinaryAny();
307     uno_copyAndConvertData(
308         out.get(), &in,
309         css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(),
310         cppToBinaryMapping_.get());
311     return out;
312 }
313 
314 uno_ThreadPool Bridge::getThreadPool() const {
315     OSL_ASSERT(threadPool_ != 0);
316     return threadPool_;
317 }
318 
319 rtl::Reference< Writer > Bridge::getWriter() {
320     osl::MutexGuard g(mutex_);
321     if (terminated_) {
322         throw css::lang::DisposedException(
323             rtl::OUString(
324                 RTL_CONSTASCII_USTRINGPARAM(
325                     "Binary URP bridge already disposed")),
326             static_cast< cppu::OWeakObject * >(this));
327     }
328     OSL_ASSERT(writer_.is());
329     return writer_;
330 }
331 
332 css::uno::UnoInterfaceReference Bridge::registerIncomingInterface(
333     rtl::OUString const & oid, css::uno::TypeDescription const & type)
334 {
335     OSL_ASSERT(type.is());
336     if (oid.getLength() == 0) {
337         return css::uno::UnoInterfaceReference();
338     }
339     css::uno::UnoInterfaceReference obj(findStub(oid, type));
340     if (!obj.is()) {
341         binaryUno_.get()->pExtEnv->getRegisteredInterface(
342             binaryUno_.get()->pExtEnv,
343             reinterpret_cast< void ** >(&obj.m_pUnoI), oid.pData,
344             reinterpret_cast< typelib_InterfaceTypeDescription * >(type.get()));
345         if (obj.is()) {
346             makeReleaseCall(oid, type);
347         } else {
348             obj.set(new Proxy(this, oid, type), SAL_NO_ACQUIRE);
349             {
350                 osl::MutexGuard g(mutex_);
351                 OSL_ASSERT(
352                     proxies_ < std::numeric_limits< std::size_t >::max());
353                 ++proxies_;
354             }
355             binaryUno_.get()->pExtEnv->registerProxyInterface(
356                 binaryUno_.get()->pExtEnv,
357                 reinterpret_cast< void ** >(&obj.m_pUnoI), &freeProxyCallback,
358                 oid.pData,
359                 reinterpret_cast< typelib_InterfaceTypeDescription * >(
360                     type.get()));
361         }
362     }
363     return obj;
364 }
365 
366 rtl::OUString Bridge::registerOutgoingInterface(
367     css::uno::UnoInterfaceReference const & object,
368     css::uno::TypeDescription const & type)
369 {
370     OSL_ASSERT(type.is());
371     if (!object.is()) {
372         return rtl::OUString();
373     }
374     rtl::OUString oid;
375     if (!Proxy::isProxy(this, object, &oid)) {
376         binaryUno_.get()->pExtEnv->getObjectIdentifier(
377             binaryUno_.get()->pExtEnv, &oid.pData, object.get());
378         osl::MutexGuard g(mutex_);
379         Stubs::iterator i(stubs_.find(oid));
380         Stub newStub;
381         Stub * stub = i == stubs_.end() ? &newStub : &i->second;
382         Stub::iterator j(stub->find(type));
383         //TODO: Release sub-stub if it is not successfully sent to remote side
384         // (otherwise, stub will leak until terminate()):
385         if (j == stub->end()) {
386             j = stub->insert(Stub::value_type(type, SubStub())).first;
387             if (stub == &newStub) {
388                 i = stubs_.insert(Stubs::value_type(oid, Stub())).first;
389                 std::swap(i->second, newStub);
390                 j = i->second.find(type);
391                 OSL_ASSERT(j !=  i->second.end());
392             }
393             j->second.object = object;
394             j->second.references = 1;
395             binaryUno_.get()->pExtEnv->registerInterface(
396                 binaryUno_.get()->pExtEnv,
397                 reinterpret_cast< void ** >(&j->second.object.m_pUnoI),
398                 oid.pData,
399                 reinterpret_cast< typelib_InterfaceTypeDescription * >(
400                     type.get()));
401         } else {
402             OSL_ASSERT(stub != &newStub);
403             if (j->second.references == SAL_MAX_UINT32) {
404                 throw css::uno::RuntimeException(
405                     rtl::OUString(
406                         RTL_CONSTASCII_USTRINGPARAM(
407                             "URP: stub reference count overflow")),
408                     css::uno::Reference< css::uno::XInterface >());
409             }
410             ++j->second.references;
411         }
412     }
413     return oid;
414 }
415 
416 css::uno::UnoInterfaceReference Bridge::findStub(
417     rtl::OUString const & oid, css::uno::TypeDescription const & type)
418 {
419     OSL_ASSERT(oid.getLength() != 0 && type.is());
420     osl::MutexGuard g(mutex_);
421     Stubs::iterator i(stubs_.find(oid));
422     if (i != stubs_.end()) {
423         Stub::iterator j(i->second.find(type));
424         if (j != i->second.end()) {
425             return j->second.object;
426         }
427         for (j = i->second.begin(); j != i->second.end(); ++j) {
428             if (typelib_typedescription_isAssignableFrom(
429                     type.get(), j->first.get()))
430             {
431                 return j->second.object;
432             }
433         }
434     }
435     return css::uno::UnoInterfaceReference();
436 }
437 
438 void Bridge::releaseStub(
439     rtl::OUString const & oid, css::uno::TypeDescription const & type)
440 {
441     OSL_ASSERT(oid.getLength() != 0 && type.is());
442     css::uno::UnoInterfaceReference obj;
443     bool unused;
444     {
445         osl::MutexGuard g(mutex_);
446         Stubs::iterator i(stubs_.find(oid));
447         if (i == stubs_.end()) {
448             throw css::uno::RuntimeException(
449                 rtl::OUString(
450                     RTL_CONSTASCII_USTRINGPARAM("URP: release unknown stub")),
451                 css::uno::Reference< css::uno::XInterface >());
452         }
453         Stub::iterator j(i->second.find(type));
454         if (j == i->second.end()) {
455             throw css::uno::RuntimeException(
456                 rtl::OUString(
457                     RTL_CONSTASCII_USTRINGPARAM("URP: release unknown stub")),
458                 css::uno::Reference< css::uno::XInterface >());
459         }
460         OSL_ASSERT(j->second.references > 0);
461         --j->second.references;
462         if (j->second.references == 0) {
463             obj = j->second.object;
464             i->second.erase(j);
465             if (i->second.empty()) {
466                 stubs_.erase(i);
467             }
468         }
469         unused = becameUnused();
470     }
471     if (obj.is()) {
472         binaryUno_.get()->pExtEnv->revokeInterface(
473             binaryUno_.get()->pExtEnv, obj.get());
474     }
475     terminateWhenUnused(unused);
476 }
477 
478 void Bridge::resurrectProxy(Proxy & proxy) {
479     uno_Interface * p = &proxy;
480     binaryUno_.get()->pExtEnv->registerProxyInterface(
481         binaryUno_.get()->pExtEnv,
482         reinterpret_cast< void ** >(&p), &freeProxyCallback,
483         proxy.getOid().pData,
484         reinterpret_cast< typelib_InterfaceTypeDescription * >(
485             proxy.getType().get()));
486     OSL_ASSERT(p == &proxy);
487 }
488 
489 void Bridge::revokeProxy(Proxy & proxy) {
490     binaryUno_.get()->pExtEnv->revokeInterface(
491         binaryUno_.get()->pExtEnv, &proxy);
492 }
493 
494 void Bridge::freeProxy(Proxy & proxy) {
495     try {
496         makeReleaseCall(proxy.getOid(), proxy.getType());
497     } catch (css::uno::RuntimeException & e) {
498         OSL_TRACE(
499             OSL_LOG_PREFIX "caught runtime exception '%s'",
500             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr());
501     } catch (std::exception & e) {
502         OSL_TRACE(OSL_LOG_PREFIX "caught C++ exception '%s'", e.what());
503     }
504     bool unused;
505     {
506         osl::MutexGuard g(mutex_);
507         OSL_ASSERT(proxies_ > 0);
508         --proxies_;
509         unused = becameUnused();
510     }
511     terminateWhenUnused(unused);
512 }
513 
514 void Bridge::incrementCalls(bool normalCall) throw () {
515     osl::MutexGuard g(mutex_);
516     OSL_ASSERT(calls_ < std::numeric_limits< std::size_t >::max());
517     ++calls_;
518     normalCall_ |= normalCall;
519 }
520 
521 void Bridge::decrementCalls() {
522     bool unused;
523     {
524         osl::MutexGuard g(mutex_);
525         OSL_ASSERT(calls_ > 0);
526         --calls_;
527         unused = becameUnused();
528     }
529     terminateWhenUnused(unused);
530 }
531 
532 void Bridge::incrementActiveCalls() throw () {
533     osl::MutexGuard g(mutex_);
534     OSL_ASSERT(
535         activeCalls_ <= calls_ &&
536         activeCalls_ < std::numeric_limits< std::size_t >::max());
537     ++activeCalls_;
538     passive_.reset();
539 }
540 
541 void Bridge::decrementActiveCalls() throw () {
542     osl::MutexGuard g(mutex_);
543     OSL_ASSERT(activeCalls_ <= calls_ && activeCalls_ > 0);
544     --activeCalls_;
545     if (activeCalls_ == 0) {
546         passive_.set();
547     }
548 }
549 
550 bool Bridge::makeCall(
551     rtl::OUString const & oid, css::uno::TypeDescription const & member,
552     bool setter, std::vector< BinaryAny > const & inArguments,
553     BinaryAny * returnValue, std::vector< BinaryAny > * outArguments)
554 {
555     std::auto_ptr< IncomingReply > resp;
556     {
557         AttachThread att(threadPool_);
558         PopOutgoingRequest pop(
559             outgoingRequests_, att.getTid(),
560             OutgoingRequest(OutgoingRequest::KIND_NORMAL, member, setter));
561         sendRequest(
562             att.getTid(), oid, css::uno::TypeDescription(), member,
563             inArguments);
564         pop.clear();
565         incrementCalls(true);
566         incrementActiveCalls();
567         void * job;
568         uno_threadpool_enter(threadPool_, &job);
569         resp.reset(static_cast< IncomingReply * >(job));
570         decrementActiveCalls();
571         decrementCalls();
572     }
573     if (resp.get() == 0) {
574         throw css::lang::DisposedException(
575             rtl::OUString(
576                 RTL_CONSTASCII_USTRINGPARAM(
577                     "Binary URP bridge disposed during call")),
578             static_cast< cppu::OWeakObject * >(this));
579     }
580     *returnValue = resp->returnValue;
581     if (!resp->exception) {
582         *outArguments = resp->outArguments;
583     }
584     return resp->exception;
585 }
586 
587 void Bridge::sendRequestChangeRequest() {
588     OSL_ASSERT(mode_ == MODE_REQUESTED);
589     random_ = random();
590     std::vector< BinaryAny > a;
591     a.push_back(
592         BinaryAny(
593             css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()),
594             &random_));
595     sendProtPropRequest(OutgoingRequest::KIND_REQUEST_CHANGE, a);
596 }
597 
598 void Bridge::handleRequestChangeReply(
599     bool exception, BinaryAny const & returnValue)
600 {
601     throwException(exception, returnValue);
602     sal_Int32 n = *static_cast< sal_Int32 * >(
603         returnValue.getValue(
604             css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get())));
605     sal_Int32 exp = 0;
606     switch (mode_) {
607     case MODE_REQUESTED:
608     case MODE_REPLY_1:
609         exp = 1;
610         break;
611     case MODE_REPLY_MINUS1:
612         exp = -1;
613         mode_ = MODE_REQUESTED;
614         break;
615     case MODE_REPLY_0:
616         exp = 0;
617         mode_ = MODE_WAIT;
618         break;
619     default:
620         OSL_ASSERT(false); // this cannot happen
621         break;
622     }
623     if (n != exp) {
624         throw css::uno::RuntimeException(
625             rtl::OUString(
626                 RTL_CONSTASCII_USTRINGPARAM(
627                     "URP: requestChange reply with unexpected return value"
628                     " received")),
629             static_cast< cppu::OWeakObject * >(this));
630     }
631     decrementCalls();
632     switch (exp) {
633     case -1:
634         sendRequestChangeRequest();
635         break;
636     case 0:
637         break;
638     case 1:
639         sendCommitChangeRequest();
640         break;
641     default:
642         OSL_ASSERT(false); // this cannot happen
643         break;
644     }
645 }
646 
647 void Bridge::handleCommitChangeReply(
648     bool exception, BinaryAny const & returnValue)
649 {
650     bool ccMode = true;
651     try {
652         throwException(exception, returnValue);
653     } catch (css::bridge::InvalidProtocolChangeException &) {
654         ccMode = false;
655     }
656     if (ccMode) {
657         setCurrentContextMode();
658     }
659     OSL_ASSERT(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1);
660     mode_ = MODE_NORMAL;
661     getWriter()->unblock();
662     decrementCalls();
663 }
664 
665 void Bridge::handleRequestChangeRequest(
666     rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments)
667 {
668     OSL_ASSERT(inArguments.size() == 1);
669     switch (mode_) {
670     case MODE_REQUESTED:
671         {
672             sal_Int32 n2 = *static_cast< sal_Int32 * >(
673                 inArguments[0].getValue(
674                     css::uno::TypeDescription(
675                         cppu::UnoType< sal_Int32 >::get())));
676             sal_Int32 ret;
677             if (n2 > random_) {
678                 ret = 1;
679                 mode_ = MODE_REPLY_0;
680             } else if (n2 == random_) {
681                 ret = -1;
682                 mode_ = MODE_REPLY_MINUS1;
683             } else {
684                 ret = 0;
685                 mode_ = MODE_REPLY_1;
686             }
687             getWriter()->sendDirectReply(
688                 tid, protPropRequest_, false,
689                 BinaryAny(
690                     css::uno::TypeDescription(
691                         cppu::UnoType< sal_Int32 >::get()),
692                     &ret),
693             std::vector< BinaryAny >());
694             break;
695         }
696     case MODE_NORMAL:
697         {
698             mode_ = MODE_NORMAL_WAIT;
699             sal_Int32 ret = 1;
700             getWriter()->queueReply(
701                 tid, protPropRequest_, false, false,
702                 BinaryAny(
703                     css::uno::TypeDescription(
704                         cppu::UnoType< sal_Int32 >::get()),
705                     &ret),
706             std::vector< BinaryAny >(), false);
707             break;
708         }
709     default:
710         throw css::uno::RuntimeException(
711             rtl::OUString(
712                 RTL_CONSTASCII_USTRINGPARAM(
713                     "URP: unexpected requestChange request received")),
714             static_cast< cppu::OWeakObject * >(this));
715     }
716 }
717 
718 void Bridge::handleCommitChangeRequest(
719     rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments)
720 {
721     bool ccMode = false;
722     bool exc = false;
723     BinaryAny ret;
724     OSL_ASSERT(inArguments.size() == 1);
725     css::uno::Sequence< css::bridge::ProtocolProperty > s;
726     OSL_VERIFY(mapBinaryToCppAny(inArguments[0]) >>= s);
727     for (sal_Int32 i = 0; i != s.getLength(); ++i) {
728         if (s[i].Name.equalsAsciiL(
729                 RTL_CONSTASCII_STRINGPARAM("CurrentContext")))
730         {
731             ccMode = true;
732         } else {
733             ccMode = false;
734             exc = true;
735             ret = mapCppToBinaryAny(
736                 css::uno::makeAny(
737                     css::bridge::InvalidProtocolChangeException(
738                         rtl::OUString(
739                             RTL_CONSTASCII_USTRINGPARAM(
740                                 "InvalidProtocolChangeException")),
741                         css::uno::Reference< css::uno::XInterface >(), s[i],
742                         1)));
743             break;
744         }
745     }
746     switch (mode_) {
747     case MODE_WAIT:
748         getWriter()->sendDirectReply(
749             tid, protPropCommit_, exc, ret, std::vector< BinaryAny >());
750         if (ccMode) {
751             setCurrentContextMode();
752             mode_ = MODE_NORMAL;
753             getWriter()->unblock();
754         } else {
755             mode_ = MODE_REQUESTED;
756             sendRequestChangeRequest();
757         }
758         break;
759     case MODE_NORMAL_WAIT:
760         getWriter()->queueReply(
761             tid, protPropCommit_, false, false, ret, std::vector< BinaryAny >(),
762             ccMode);
763         mode_ = MODE_NORMAL;
764         break;
765     default:
766         throw css::uno::RuntimeException(
767             rtl::OUString(
768                 RTL_CONSTASCII_USTRINGPARAM(
769                     "URP: unexpected commitChange request received")),
770             static_cast< cppu::OWeakObject * >(this));
771     }
772 }
773 
774 OutgoingRequest Bridge::lastOutgoingRequest(rtl::ByteSequence const & tid) {
775     OutgoingRequest req(outgoingRequests_.top(tid));
776     outgoingRequests_.pop(tid);
777     return req;
778 }
779 
780 bool Bridge::isProtocolPropertiesRequest(
781     rtl::OUString const & oid, css::uno::TypeDescription const & type) const
782 {
783     return oid == protPropOid_ && type.equals(protPropType_);
784 }
785 
786 void Bridge::setCurrentContextMode() {
787     osl::MutexGuard g(mutex_);
788     currentContextMode_ = true;
789 }
790 
791 bool Bridge::isCurrentContextMode() {
792     osl::MutexGuard g(mutex_);
793     return currentContextMode_;
794 }
795 
796 Bridge::~Bridge() {
797     if (threadPool_ != 0) {
798         uno_threadpool_destroy(threadPool_);
799     }
800 }
801 
802 css::uno::Reference< css::uno::XInterface > Bridge::getInstance(
803     rtl::OUString const & sInstanceName) throw (css::uno::RuntimeException)
804 {
805     if (sInstanceName.getLength() == 0) {
806         throw css::uno::RuntimeException(
807             rtl::OUString(
808                 RTL_CONSTASCII_USTRINGPARAM(
809                     "XBridge::getInstance sInstanceName must be non-empty")),
810             static_cast< cppu::OWeakObject * >(this));
811     }
812     for (sal_Int32 i = 0; i != sInstanceName.getLength(); ++i) {
813         if (sInstanceName[i] > 0x7F) {
814             throw css::io::IOException(
815                 rtl::OUString(
816                     RTL_CONSTASCII_USTRINGPARAM(
817                         "XBridge::getInstance sInstanceName contains non-ASCII"
818                         " character")),
819                 css::uno::Reference< css::uno::XInterface >());
820         }
821     }
822     css::uno::TypeDescription ifc(
823         cppu::UnoType< css::uno::Reference< css::uno::XInterface > >::get());
824     typelib_TypeDescription * p = ifc.get();
825     std::vector< BinaryAny > inArgs;
826     inArgs.push_back(
827         BinaryAny(
828             css::uno::TypeDescription(cppu::UnoType< css::uno::Type >::get()),
829             &p));
830     BinaryAny ret;
831     std::vector< BinaryAny> outArgs;
832     bool exc = makeCall(
833         sInstanceName,
834         css::uno::TypeDescription(
835             rtl::OUString(
836                 RTL_CONSTASCII_USTRINGPARAM(
837                     "com.sun.star.uno.XInterface::queryInterface"))),
838         false, inArgs, &ret, &outArgs);
839     throwException(exc, ret);
840     return css::uno::Reference< css::uno::XInterface >(
841         static_cast< css::uno::XInterface * >(
842             binaryToCppMapping_.mapInterface(
843                 *static_cast< uno_Interface ** >(ret.getValue(ifc)),
844                 ifc.get())),
845         css::uno::UNO_REF_NO_ACQUIRE);
846 }
847 
848 rtl::OUString Bridge::getName() throw (css::uno::RuntimeException) {
849     return name_;
850 }
851 
852 rtl::OUString Bridge::getDescription() throw (css::uno::RuntimeException) {
853     rtl::OUStringBuffer b(name_);
854     b.append(sal_Unicode(':'));
855     b.append(connection_->getDescription());
856     return b.makeStringAndClear();
857 }
858 
859 void Bridge::dispose() throw (css::uno::RuntimeException) {
860     terminate();
861     // OOo expects dispose to not return while there are still remote calls in
862     // progress; an external protocol must ensure that dispose is not called
863     // from within an incoming or outgoing remote call, as passive_.wait() would
864     // otherwise deadlock:
865     passive_.wait();
866 }
867 
868 void Bridge::addEventListener(
869     css::uno::Reference< css::lang::XEventListener > const & xListener)
870     throw (css::uno::RuntimeException)
871 {
872     OSL_ASSERT(xListener.is());
873     {
874         osl::MutexGuard g(mutex_);
875         if (!terminated_) {
876             listeners_.push_back(xListener);
877             return;
878         }
879     }
880     xListener->disposing(
881         css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
882 }
883 
884 void Bridge::removeEventListener(
885     css::uno::Reference< css::lang::XEventListener > const & aListener)
886     throw (css::uno::RuntimeException)
887 {
888     osl::MutexGuard g(mutex_);
889     Listeners::iterator i(
890         std::find(listeners_.begin(), listeners_.end(), aListener));
891     if (i != listeners_.end()) {
892         listeners_.erase(i);
893     }
894 }
895 
896 void Bridge::sendCommitChangeRequest() {
897     OSL_ASSERT(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1);
898     css::uno::Sequence< css::bridge::ProtocolProperty > s(1);
899     s[0].Name = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CurrentContext"));
900     std::vector< BinaryAny > a;
901     a.push_back(mapCppToBinaryAny(css::uno::makeAny(s)));
902     sendProtPropRequest(OutgoingRequest::KIND_COMMIT_CHANGE, a);
903 }
904 
905 void Bridge::sendProtPropRequest(
906     OutgoingRequest::Kind kind, std::vector< BinaryAny > const & inArguments)
907 {
908     OSL_ASSERT(
909         kind == OutgoingRequest::KIND_REQUEST_CHANGE ||
910         kind == OutgoingRequest::KIND_COMMIT_CHANGE);
911     incrementCalls(false);
912     css::uno::TypeDescription member(
913         kind == OutgoingRequest::KIND_REQUEST_CHANGE
914         ? protPropRequest_ : protPropCommit_);
915     PopOutgoingRequest pop(
916         outgoingRequests_, protPropTid_, OutgoingRequest(kind, member, false));
917     getWriter()->sendDirectRequest(
918         protPropTid_, protPropOid_, protPropType_, member, inArguments);
919     pop.clear();
920 }
921 
922 void Bridge::makeReleaseCall(
923     rtl::OUString const & oid, css::uno::TypeDescription const & type)
924 {
925     AttachThread att(threadPool_);
926     sendRequest(
927         att.getTid(), oid, type,
928         css::uno::TypeDescription(
929             rtl::OUString(
930                 RTL_CONSTASCII_USTRINGPARAM(
931                     "com.sun.star.uno.XInterface::release"))),
932         std::vector< BinaryAny >());
933 }
934 
935 void Bridge::sendRequest(
936     rtl::ByteSequence const & tid, rtl::OUString const & oid,
937     css::uno::TypeDescription const & type,
938     css::uno::TypeDescription const & member,
939     std::vector< BinaryAny > const & inArguments)
940 {
941     getWriter()->queueRequest(tid, oid, type, member, inArguments);
942 }
943 
944 void Bridge::throwException(bool exception, BinaryAny const & value) {
945     if (exception) {
946         cppu::throwException(mapBinaryToCppAny(value));
947     }
948 }
949 
950 css::uno::Any Bridge::mapBinaryToCppAny(BinaryAny const & binaryAny) {
951     BinaryAny in(binaryAny);
952     css::uno::Any out;
953     out.~Any();
954     uno_copyAndConvertData(
955         &out, in.get(),
956         css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(),
957         binaryToCppMapping_.get());
958     return out;
959 }
960 
961 bool Bridge::becameUnused() const {
962     return stubs_.empty() && proxies_ == 0 && calls_ == 0 && normalCall_;
963 }
964 
965 void Bridge::terminateWhenUnused(bool unused) {
966     if (unused) {
967         // That the current thread considers the bridge unused implies that it
968         // is not within an incoming or outgoing remote call (so calling
969         // terminate cannot lead to deadlock):
970         terminate();
971     }
972 }
973 
974 }
975