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