Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / binaryurp / source / bridge.cxx
blob8b086b71119bd9aabc5decc3bac65966005ddc7d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <cassert>
24 #include <cstddef>
25 #include <limits>
26 #include <memory>
27 #include <vector>
29 #include <com/sun/star/bridge/InvalidProtocolChangeException.hpp>
30 #include <com/sun/star/bridge/XBridge.hpp>
31 #include <com/sun/star/bridge/XInstanceProvider.hpp>
32 #include <com/sun/star/bridge/XProtocolProperties.hpp>
33 #include <com/sun/star/connection/XConnection.hpp>
34 #include <com/sun/star/io/IOException.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/lang/EventObject.hpp>
37 #include <com/sun/star/lang/XEventListener.hpp>
38 #include <com/sun/star/uno/Reference.hxx>
39 #include <com/sun/star/uno/RuntimeException.hpp>
40 #include <com/sun/star/uno/Sequence.hxx>
41 #include <com/sun/star/uno/XInterface.hpp>
42 #include <cppuhelper/exc_hlp.hxx>
43 #include <cppuhelper/weak.hxx>
44 #include <osl/mutex.hxx>
45 #include <osl/thread.hxx>
46 #include <rtl/byteseq.hxx>
47 #include <rtl/random.h>
48 #include <rtl/ref.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <rtl/ustring.h>
51 #include <rtl/ustring.hxx>
52 #include <sal/log.hxx>
53 #include <sal/types.h>
54 #include <typelib/typeclass.h>
55 #include <typelib/typedescription.h>
56 #include <typelib/typedescription.hxx>
57 #include <uno/dispatcher.hxx>
58 #include <uno/environment.hxx>
59 #include <uno/lbnames.h>
61 #include "binaryany.hxx"
62 #include "bridge.hxx"
63 #include "bridgefactory.hxx"
64 #include "incomingreply.hxx"
65 #include "lessoperators.hxx"
66 #include "outgoingrequest.hxx"
67 #include "outgoingrequests.hxx"
68 #include "proxy.hxx"
69 #include "reader.hxx"
71 namespace binaryurp {
73 namespace {
75 sal_Int32 random() {
76 sal_Int32 n;
77 rtlRandomPool pool = rtl_random_createPool();
78 rtl_random_getBytes(pool, &n, sizeof n);
79 rtl_random_destroyPool(pool);
80 return n;
83 OUString toString(css::uno::TypeDescription const & type) {
84 typelib_TypeDescription * d = type.get();
85 assert(d != nullptr && d->pTypeName != nullptr);
86 return OUString(d->pTypeName);
89 extern "C" void freeProxyCallback(
90 SAL_UNUSED_PARAMETER uno_ExtEnvironment *, void * pProxy)
92 assert(pProxy != nullptr);
93 static_cast< Proxy * >(pProxy)->do_free();
96 bool isThread(salhelper::Thread * thread) {
97 assert(thread != nullptr);
98 return osl::Thread::getCurrentIdentifier() == thread->getIdentifier();
101 class AttachThread {
102 public:
103 explicit AttachThread(uno_ThreadPool threadPool);
105 ~AttachThread();
107 const rtl::ByteSequence& getTid() const throw () { return tid_;}
109 private:
110 AttachThread(const AttachThread&) = delete;
111 AttachThread& operator=(const AttachThread&) = delete;
113 uno_ThreadPool threadPool_;
114 rtl::ByteSequence tid_;
117 AttachThread::AttachThread(uno_ThreadPool threadPool): threadPool_(threadPool) {
118 sal_Sequence * s = nullptr;
119 uno_getIdOfCurrentThread(&s);
120 tid_ = rtl::ByteSequence(s, rtl::BYTESEQ_NOACQUIRE);
121 uno_threadpool_attach(threadPool_);
124 AttachThread::~AttachThread() {
125 uno_threadpool_detach(threadPool_);
126 uno_releaseIdFromCurrentThread();
130 class PopOutgoingRequest {
131 public:
132 PopOutgoingRequest(
133 OutgoingRequests & requests, rtl::ByteSequence const & tid,
134 OutgoingRequest const & request);
136 ~PopOutgoingRequest();
138 void clear();
140 private:
141 PopOutgoingRequest(const PopOutgoingRequest&) = delete;
142 PopOutgoingRequest& operator=(const PopOutgoingRequest&) = delete;
144 OutgoingRequests & requests_;
145 rtl::ByteSequence tid_;
146 bool cleared_;
149 PopOutgoingRequest::PopOutgoingRequest(
150 OutgoingRequests & requests, rtl::ByteSequence const & tid,
151 OutgoingRequest const & request):
152 requests_(requests), tid_(tid), cleared_(false)
154 requests_.push(tid_, request);
157 PopOutgoingRequest::~PopOutgoingRequest() {
158 if (!cleared_) {
159 requests_.pop(tid_);
163 void PopOutgoingRequest::clear() {
164 cleared_ = true;
169 struct Bridge::SubStub {
170 com::sun::star::uno::UnoInterfaceReference object;
172 sal_uInt32 references;
175 Bridge::Bridge(
176 rtl::Reference< BridgeFactory > const & factory, OUString const & name,
177 css::uno::Reference< css::connection::XConnection > const & connection,
178 css::uno::Reference< css::bridge::XInstanceProvider > const & provider):
179 factory_(factory), name_(name), connection_(connection),
180 provider_(provider),
181 binaryUno_(UNO_LB_UNO),
182 cppToBinaryMapping_(CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO),
183 binaryToCppMapping_(UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME),
184 protPropTid_(
185 reinterpret_cast< sal_Int8 const * >(".UrpProtocolPropertiesTid"),
186 RTL_CONSTASCII_LENGTH(".UrpProtocolPropertiesTid")),
187 protPropOid_("UrpProtocolProperties"),
188 protPropType_(
189 cppu::UnoType<
190 css::uno::Reference< css::bridge::XProtocolProperties > >::get()),
191 protPropRequest_("com.sun.star.bridge.XProtocolProperties::requestChange"),
192 protPropCommit_("com.sun.star.bridge.XProtocolProperties::commitChange"),
193 state_(STATE_INITIAL), threadPool_(nullptr), currentContextMode_(false),
194 proxies_(0), calls_(0), normalCall_(false), activeCalls_(0),
195 mode_(MODE_REQUESTED)
197 assert(factory.is() && connection.is());
198 if (!binaryUno_.is()) {
199 throw css::uno::RuntimeException("URP: no binary UNO environment");
201 if (!(cppToBinaryMapping_.is() && binaryToCppMapping_.is())) {
202 throw css::uno::RuntimeException("URP: no C++ UNO mapping");
204 passive_.set();
207 void Bridge::start() {
208 rtl::Reference r(new Reader(this));
209 rtl::Reference w(new Writer(this));
211 osl::MutexGuard g(mutex_);
212 assert(
213 state_ == STATE_INITIAL && threadPool_ == nullptr && !writer_.is() &&
214 !reader_.is());
215 threadPool_ = uno_threadpool_create();
216 assert(threadPool_ != nullptr);
217 reader_ = r;
218 writer_ = w;
219 state_ = STATE_STARTED;
221 // It is important to call reader_->launch() last here; both
222 // Writer::execute and Reader::execute can call Bridge::terminate, but
223 // Writer::execute is initially blocked in unblocked_.wait() until
224 // Reader::execute has called bridge_->sendRequestChangeRequest(), so
225 // effectively only reader_->launch() can lead to an early call to
226 // Bridge::terminate
227 w->launch();
228 r->launch();
231 void Bridge::terminate(bool final) {
232 uno_ThreadPool tp;
233 // Make sure function-local variables (Stubs s, etc.) are destroyed before
234 // the final uno_threadpool_destroy/threadPool_ = 0:
236 rtl::Reference< Reader > r;
237 rtl::Reference< Writer > w;
238 bool joinW;
239 Listeners ls;
241 osl::ClearableMutexGuard g(mutex_);
242 switch (state_) {
243 case STATE_INITIAL: // via ~Bridge -> dispose -> terminate
244 case STATE_FINAL:
245 return;
246 case STATE_STARTED:
247 break;
248 case STATE_TERMINATED:
249 if (final) {
250 g.clear();
251 terminated_.wait();
253 osl::MutexGuard g2(mutex_);
254 tp = threadPool_;
255 threadPool_ = nullptr;
256 if (reader_.is()) {
257 if (!isThread(reader_.get())) {
258 r = reader_;
260 reader_.clear();
262 if (writer_.is()) {
263 if (!isThread(writer_.get())) {
264 w = writer_;
266 writer_.clear();
268 state_ = STATE_FINAL;
270 assert(!(r.is() && w.is()));
271 if (r.is()) {
272 r->join();
273 } else if (w.is()) {
274 w->join();
276 if (tp != nullptr) {
277 uno_threadpool_destroy(tp);
280 return;
282 tp = threadPool_;
283 assert(!(final && isThread(reader_.get())));
284 if (!isThread(reader_.get())) {
285 std::swap(reader_, r);
287 w = writer_;
288 joinW = !isThread(writer_.get());
289 assert(!final || joinW);
290 if (joinW) {
291 writer_.clear();
293 ls.swap(listeners_);
294 state_ = final ? STATE_FINAL : STATE_TERMINATED;
296 try {
297 connection_->close();
298 } catch (const css::io::IOException & e) {
299 SAL_INFO("binaryurp", "caught IO exception '" << e << '\'');
301 assert(w.is());
302 w->stop();
303 if (r.is()) {
304 r->join();
306 if (joinW) {
307 w->join();
309 assert(tp != nullptr);
310 uno_threadpool_dispose(tp);
311 Stubs s;
313 osl::MutexGuard g(mutex_);
314 s.swap(stubs_);
316 for (auto & stub : s)
318 for (auto & item : stub.second)
320 SAL_INFO(
321 "binaryurp",
322 "stub '" << stub.first << "', '" << toString(item.first)
323 << "' still mapped at Bridge::terminate");
324 binaryUno_.get()->pExtEnv->revokeInterface(
325 binaryUno_.get()->pExtEnv, item.second.object.get());
328 factory_->removeBridge(this);
329 for (auto const& listener : ls)
331 try {
332 listener->disposing(
333 css::lang::EventObject(
334 static_cast< cppu::OWeakObject * >(this)));
335 } catch (const css::uno::RuntimeException & e) {
336 SAL_WARN("binaryurp", "caught " << e);
340 if (final) {
341 uno_threadpool_destroy(tp);
344 osl::MutexGuard g(mutex_);
345 if (final) {
346 threadPool_ = nullptr;
349 terminated_.set();
353 BinaryAny Bridge::mapCppToBinaryAny(css::uno::Any const & cppAny) {
354 css::uno::Any in(cppAny);
355 BinaryAny out;
356 out.~BinaryAny();
357 uno_copyAndConvertData(
358 &out.get(), &in,
359 css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(),
360 cppToBinaryMapping_.get());
361 return out;
364 uno_ThreadPool Bridge::getThreadPool() {
365 osl::MutexGuard g(mutex_);
366 checkDisposed();
367 assert(threadPool_ != nullptr);
368 return threadPool_;
371 rtl::Reference< Writer > Bridge::getWriter() {
372 osl::MutexGuard g(mutex_);
373 checkDisposed();
374 assert(writer_.is());
375 return writer_;
378 css::uno::UnoInterfaceReference Bridge::registerIncomingInterface(
379 OUString const & oid, css::uno::TypeDescription const & type)
381 assert(type.is());
382 if (oid.isEmpty()) {
383 return css::uno::UnoInterfaceReference();
385 css::uno::UnoInterfaceReference obj(findStub(oid, type));
386 if (!obj.is()) {
387 binaryUno_.get()->pExtEnv->getRegisteredInterface(
388 binaryUno_.get()->pExtEnv,
389 reinterpret_cast< void ** >(&obj.m_pUnoI), oid.pData,
390 reinterpret_cast< typelib_InterfaceTypeDescription * >(type.get()));
391 if (obj.is()) {
392 makeReleaseCall(oid, type);
393 } else {
394 obj.set(new Proxy(this, oid, type), SAL_NO_ACQUIRE);
396 osl::MutexGuard g(mutex_);
397 assert(proxies_ < std::numeric_limits< std::size_t >::max());
398 ++proxies_;
400 binaryUno_.get()->pExtEnv->registerProxyInterface(
401 binaryUno_.get()->pExtEnv,
402 reinterpret_cast< void ** >(&obj.m_pUnoI), &freeProxyCallback,
403 oid.pData,
404 reinterpret_cast< typelib_InterfaceTypeDescription * >(
405 type.get()));
408 return obj;
411 OUString Bridge::registerOutgoingInterface(
412 css::uno::UnoInterfaceReference const & object,
413 css::uno::TypeDescription const & type)
415 assert(type.is());
416 if (!object.is()) {
417 return OUString();
419 OUString oid;
420 if (!Proxy::isProxy(this, object, &oid)) {
421 binaryUno_.get()->pExtEnv->getObjectIdentifier(
422 binaryUno_.get()->pExtEnv, &oid.pData, object.get());
423 osl::MutexGuard g(mutex_);
424 Stubs::iterator i(stubs_.find(oid));
425 Stub newStub;
426 Stub * stub = i == stubs_.end() ? &newStub : &i->second;
427 Stub::iterator j(stub->find(type));
428 //TODO: Release sub-stub if it is not successfully sent to remote side
429 // (otherwise, stub will leak until terminate()):
430 if (j == stub->end()) {
431 j = stub->emplace(type, SubStub()).first;
432 if (stub == &newStub) {
433 i = stubs_.emplace(oid, Stub()).first;
434 std::swap(i->second, newStub);
435 j = i->second.find(type);
436 assert(j != i->second.end());
438 j->second.object = object;
439 j->second.references = 1;
440 binaryUno_.get()->pExtEnv->registerInterface(
441 binaryUno_.get()->pExtEnv,
442 reinterpret_cast< void ** >(&j->second.object.m_pUnoI),
443 oid.pData,
444 reinterpret_cast< typelib_InterfaceTypeDescription * >(
445 type.get()));
446 } else {
447 assert(stub != &newStub);
448 if (j->second.references == SAL_MAX_UINT32) {
449 throw css::uno::RuntimeException(
450 "URP: stub reference count overflow");
452 ++j->second.references;
455 return oid;
458 css::uno::UnoInterfaceReference Bridge::findStub(
459 OUString const & oid, css::uno::TypeDescription const & type)
461 assert(!oid.isEmpty() && type.is());
462 osl::MutexGuard g(mutex_);
463 Stubs::iterator i(stubs_.find(oid));
464 if (i != stubs_.end()) {
465 Stub::iterator j(i->second.find(type));
466 if (j != i->second.end()) {
467 return j->second.object;
469 for (auto const& item : i->second)
471 if (typelib_typedescription_isAssignableFrom(
472 type.get(), item.first.get()))
474 return item.second.object;
478 return css::uno::UnoInterfaceReference();
481 void Bridge::releaseStub(
482 OUString const & oid, css::uno::TypeDescription const & type)
484 assert(!oid.isEmpty() && type.is());
485 css::uno::UnoInterfaceReference obj;
486 bool unused;
488 osl::MutexGuard g(mutex_);
489 Stubs::iterator i(stubs_.find(oid));
490 if (i == stubs_.end()) {
491 throw css::uno::RuntimeException("URP: release unknown stub");
493 Stub::iterator j(i->second.find(type));
494 if (j == i->second.end()) {
495 throw css::uno::RuntimeException("URP: release unknown stub");
497 assert(j->second.references > 0);
498 --j->second.references;
499 if (j->second.references == 0) {
500 obj = j->second.object;
501 i->second.erase(j);
502 if (i->second.empty()) {
503 stubs_.erase(i);
506 unused = becameUnused();
508 if (obj.is()) {
509 binaryUno_.get()->pExtEnv->revokeInterface(
510 binaryUno_.get()->pExtEnv, obj.get());
512 terminateWhenUnused(unused);
515 void Bridge::resurrectProxy(Proxy & proxy) {
516 uno_Interface * p = &proxy;
517 binaryUno_.get()->pExtEnv->registerProxyInterface(
518 binaryUno_.get()->pExtEnv,
519 reinterpret_cast< void ** >(&p), &freeProxyCallback,
520 proxy.getOid().pData,
521 reinterpret_cast< typelib_InterfaceTypeDescription * >(
522 proxy.getType().get()));
523 assert(p == &proxy);
526 void Bridge::revokeProxy(Proxy & proxy) {
527 binaryUno_.get()->pExtEnv->revokeInterface(
528 binaryUno_.get()->pExtEnv, &proxy);
531 void Bridge::freeProxy(Proxy & proxy) {
532 try {
533 makeReleaseCall(proxy.getOid(), proxy.getType());
534 } catch (const css::uno::RuntimeException & e) {
535 SAL_INFO(
536 "binaryurp", "caught runtime exception '" << e << '\'');
537 } catch (const std::exception & e) {
538 SAL_WARN("binaryurp", "caught C++ exception '" << e.what() << '\'');
540 bool unused;
542 osl::MutexGuard g(mutex_);
543 assert(proxies_ > 0);
544 --proxies_;
545 unused = becameUnused();
547 terminateWhenUnused(unused);
550 void Bridge::incrementCalls(bool normalCall) throw () {
551 osl::MutexGuard g(mutex_);
552 assert(calls_ < std::numeric_limits< std::size_t >::max());
553 ++calls_;
554 normalCall_ |= normalCall;
557 void Bridge::decrementCalls() {
558 bool unused;
560 osl::MutexGuard g(mutex_);
561 assert(calls_ > 0);
562 --calls_;
563 unused = becameUnused();
565 terminateWhenUnused(unused);
568 void Bridge::incrementActiveCalls() throw () {
569 osl::MutexGuard g(mutex_);
570 assert(
571 activeCalls_ <= calls_ &&
572 activeCalls_ < std::numeric_limits< std::size_t >::max());
573 ++activeCalls_;
574 passive_.reset();
577 void Bridge::decrementActiveCalls() throw () {
578 osl::MutexGuard g(mutex_);
579 assert(activeCalls_ <= calls_ && activeCalls_ > 0);
580 --activeCalls_;
581 if (activeCalls_ == 0) {
582 passive_.set();
586 bool Bridge::makeCall(
587 OUString const & oid, css::uno::TypeDescription const & member,
588 bool setter, std::vector< BinaryAny > const & inArguments,
589 BinaryAny * returnValue, std::vector< BinaryAny > * outArguments)
591 std::unique_ptr< IncomingReply > resp;
593 uno_ThreadPool tp = getThreadPool();
594 AttachThread att(tp);
595 PopOutgoingRequest pop(
596 outgoingRequests_, att.getTid(),
597 OutgoingRequest(OutgoingRequest::KIND_NORMAL, member, setter));
598 sendRequest(
599 att.getTid(), oid, css::uno::TypeDescription(), member,
600 inArguments);
601 pop.clear();
602 incrementCalls(true);
603 incrementActiveCalls();
604 void * job;
605 uno_threadpool_enter(tp, &job);
606 resp.reset(static_cast< IncomingReply * >(job));
607 decrementActiveCalls();
608 decrementCalls();
610 if (resp == nullptr)
612 throw css::lang::DisposedException(
613 "Binary URP bridge disposed during call",
614 static_cast< cppu::OWeakObject * >(this));
616 *returnValue = resp->returnValue;
617 if (!resp->exception) {
618 *outArguments = resp->outArguments;
620 return resp->exception;
623 void Bridge::sendRequestChangeRequest() {
624 assert(mode_ == MODE_REQUESTED);
625 random_ = random();
626 std::vector< BinaryAny > a;
627 a.emplace_back(
628 css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()),
629 &random_);
630 sendProtPropRequest(OutgoingRequest::KIND_REQUEST_CHANGE, a);
633 void Bridge::handleRequestChangeReply(
634 bool exception, BinaryAny const & returnValue)
636 try {
637 throwException(exception, returnValue);
638 } catch (css::uno::RuntimeException & e) {
639 // Before OOo 2.2, Java URP would throw a RuntimeException when
640 // receiving a requestChange message (see i#35277 "Java URP: Support
641 // Manipulation of Protocol Properties"):
642 if (mode_ != MODE_REQUESTED) {
643 throw;
645 SAL_WARN(
646 "binaryurp",
647 "requestChange caught " << e << " in state 'requested'");
648 mode_ = MODE_NORMAL;
649 getWriter()->unblock();
650 decrementCalls();
651 return;
653 sal_Int32 n = *static_cast< sal_Int32 * >(
654 returnValue.getValue(
655 css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get())));
656 sal_Int32 exp = 0;
657 switch (mode_) {
658 case MODE_REQUESTED:
659 case MODE_REPLY_1:
660 exp = 1;
661 break;
662 case MODE_REPLY_MINUS1:
663 exp = -1;
664 mode_ = MODE_REQUESTED;
665 break;
666 case MODE_REPLY_0:
667 exp = 0;
668 mode_ = MODE_WAIT;
669 break;
670 default:
671 assert(false); // this cannot happen
672 break;
674 if (n != exp) {
675 throw css::uno::RuntimeException(
676 "URP: requestChange reply with unexpected return value received",
677 static_cast< cppu::OWeakObject * >(this));
679 decrementCalls();
680 switch (exp) {
681 case -1:
682 sendRequestChangeRequest();
683 break;
684 case 0:
685 break;
686 case 1:
687 sendCommitChangeRequest();
688 break;
689 default:
690 assert(false); // this cannot happen
691 break;
695 void Bridge::handleCommitChangeReply(
696 bool exception, BinaryAny const & returnValue)
698 bool bCcMode = true;
699 try {
700 throwException(exception, returnValue);
701 } catch (const css::bridge::InvalidProtocolChangeException &) {
702 bCcMode = false;
704 if (bCcMode) {
705 setCurrentContextMode();
707 assert(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1);
708 mode_ = MODE_NORMAL;
709 getWriter()->unblock();
710 decrementCalls();
713 void Bridge::handleRequestChangeRequest(
714 rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments)
716 assert(inArguments.size() == 1);
717 switch (mode_) {
718 case MODE_REQUESTED:
720 sal_Int32 n2 = *static_cast< sal_Int32 * >(
721 inArguments[0].getValue(
722 css::uno::TypeDescription(
723 cppu::UnoType< sal_Int32 >::get())));
724 sal_Int32 ret;
725 if (n2 > random_) {
726 ret = 1;
727 mode_ = MODE_REPLY_0;
728 } else if (n2 == random_) {
729 ret = -1;
730 mode_ = MODE_REPLY_MINUS1;
731 } else {
732 ret = 0;
733 mode_ = MODE_REPLY_1;
735 getWriter()->sendDirectReply(
736 tid, protPropRequest_, false,
737 BinaryAny(
738 css::uno::TypeDescription(
739 cppu::UnoType< sal_Int32 >::get()),
740 &ret),
741 std::vector< BinaryAny >());
742 break;
744 case MODE_NORMAL:
746 mode_ = MODE_NORMAL_WAIT;
747 sal_Int32 ret = 1;
748 getWriter()->queueReply(
749 tid, protPropRequest_, false, false,
750 BinaryAny(
751 css::uno::TypeDescription(
752 cppu::UnoType< sal_Int32 >::get()),
753 &ret),
754 std::vector< BinaryAny >(), false);
755 break;
757 default:
758 throw css::uno::RuntimeException(
759 "URP: unexpected requestChange request received",
760 static_cast< cppu::OWeakObject * >(this));
764 void Bridge::handleCommitChangeRequest(
765 rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments)
767 bool bCcMode = false;
768 bool bExc = false;
769 BinaryAny ret;
770 assert(inArguments.size() == 1);
771 css::uno::Sequence< css::bridge::ProtocolProperty > s;
772 [[maybe_unused]] bool ok = (mapBinaryToCppAny(inArguments[0]) >>= s);
773 assert(ok);
774 for (sal_Int32 i = 0; i != s.getLength(); ++i) {
775 if (s[i].Name == "CurrentContext") {
776 bCcMode = true;
777 } else {
778 bCcMode = false;
779 bExc = true;
780 ret = mapCppToBinaryAny(
781 css::uno::Any(
782 css::bridge::InvalidProtocolChangeException(
783 "InvalidProtocolChangeException",
784 css::uno::Reference< css::uno::XInterface >(), s[i],
785 1)));
786 break;
789 switch (mode_) {
790 case MODE_WAIT:
791 getWriter()->sendDirectReply(
792 tid, protPropCommit_, bExc, ret, std::vector< BinaryAny >());
793 if (bCcMode) {
794 setCurrentContextMode();
795 mode_ = MODE_NORMAL;
796 getWriter()->unblock();
797 } else {
798 mode_ = MODE_REQUESTED;
799 sendRequestChangeRequest();
801 break;
802 case MODE_NORMAL_WAIT:
803 getWriter()->queueReply(
804 tid, protPropCommit_, false, false, ret, std::vector< BinaryAny >(),
805 bCcMode);
806 mode_ = MODE_NORMAL;
807 break;
808 default:
809 throw css::uno::RuntimeException(
810 "URP: unexpected commitChange request received",
811 static_cast< cppu::OWeakObject * >(this));
815 OutgoingRequest Bridge::lastOutgoingRequest(rtl::ByteSequence const & tid) {
816 OutgoingRequest req(outgoingRequests_.top(tid));
817 outgoingRequests_.pop(tid);
818 return req;
821 bool Bridge::isProtocolPropertiesRequest(
822 OUString const & oid, css::uno::TypeDescription const & type) const
824 return oid == protPropOid_ && type.equals(protPropType_);
827 void Bridge::setCurrentContextMode() {
828 osl::MutexGuard g(mutex_);
829 currentContextMode_ = true;
832 bool Bridge::isCurrentContextMode() {
833 osl::MutexGuard g(mutex_);
834 return currentContextMode_;
837 Bridge::~Bridge() {
838 #if OSL_DEBUG_LEVEL > 0
840 osl::MutexGuard g(mutex_);
841 SAL_WARN_IF(
842 state_ == STATE_STARTED || state_ == STATE_TERMINATED, "binaryurp",
843 "undisposed bridge, potential deadlock ahead");
845 #endif
846 dispose();
849 css::uno::Reference< css::uno::XInterface > Bridge::getInstance(
850 OUString const & sInstanceName)
852 if (sInstanceName.isEmpty()) {
853 throw css::uno::RuntimeException(
854 "XBridge::getInstance sInstanceName must be non-empty",
855 static_cast< cppu::OWeakObject * >(this));
857 for (sal_Int32 i = 0; i != sInstanceName.getLength(); ++i) {
858 if (sInstanceName[i] > 0x7F) {
859 throw css::uno::RuntimeException(
860 "XBridge::getInstance sInstanceName contains non-ASCII"
861 " character");
864 css::uno::TypeDescription ifc(cppu::UnoType<css::uno::XInterface>::get());
865 typelib_TypeDescription * p = ifc.get();
866 std::vector< BinaryAny > inArgs;
867 inArgs.emplace_back(
868 css::uno::TypeDescription(cppu::UnoType< css::uno::Type >::get()),
869 &p);
870 BinaryAny ret;
871 std::vector< BinaryAny> outArgs;
872 bool bExc = makeCall(
873 sInstanceName,
874 css::uno::TypeDescription(
875 "com.sun.star.uno.XInterface::queryInterface"),
876 false, inArgs, &ret, &outArgs);
877 throwException(bExc, ret);
878 return css::uno::Reference< css::uno::XInterface >(
879 static_cast< css::uno::XInterface * >(
880 binaryToCppMapping_.mapInterface(
881 *static_cast< uno_Interface ** >(ret.getValue(ifc)),
882 ifc.get())),
883 SAL_NO_ACQUIRE);
886 OUString Bridge::getName() {
887 return name_;
890 OUString Bridge::getDescription() {
891 OUString b = name_ + ":" + connection_->getDescription();
892 return b;
895 void Bridge::dispose() {
896 // For terminate(true) not to deadlock, an external protocol must ensure
897 // that dispose is not called from a thread pool worker thread (that dispose
898 // is never called from the reader or writer thread is already ensured
899 // internally):
900 terminate(true);
901 // OOo expects dispose to not return while there are still remote calls in
902 // progress; an external protocol must ensure that dispose is not called
903 // from within an incoming or outgoing remote call, as passive_.wait() would
904 // otherwise deadlock:
905 passive_.wait();
908 void Bridge::addEventListener(
909 css::uno::Reference< css::lang::XEventListener > const & xListener)
911 assert(xListener.is());
913 osl::MutexGuard g(mutex_);
914 assert(state_ != STATE_INITIAL);
915 if (state_ == STATE_STARTED) {
916 listeners_.push_back(xListener);
917 return;
920 xListener->disposing(
921 css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
924 void Bridge::removeEventListener(
925 css::uno::Reference< css::lang::XEventListener > const & aListener)
927 osl::MutexGuard g(mutex_);
928 Listeners::iterator i(
929 std::find(listeners_.begin(), listeners_.end(), aListener));
930 if (i != listeners_.end()) {
931 listeners_.erase(i);
935 void Bridge::sendCommitChangeRequest() {
936 assert(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1);
937 css::uno::Sequence< css::bridge::ProtocolProperty > s(1);
938 s[0].Name = "CurrentContext";
939 std::vector< BinaryAny > a;
940 a.push_back(mapCppToBinaryAny(css::uno::Any(s)));
941 sendProtPropRequest(OutgoingRequest::KIND_COMMIT_CHANGE, a);
944 void Bridge::sendProtPropRequest(
945 OutgoingRequest::Kind kind, std::vector< BinaryAny > const & inArguments)
947 assert(
948 kind == OutgoingRequest::KIND_REQUEST_CHANGE ||
949 kind == OutgoingRequest::KIND_COMMIT_CHANGE);
950 incrementCalls(false);
951 css::uno::TypeDescription member(
952 kind == OutgoingRequest::KIND_REQUEST_CHANGE
953 ? protPropRequest_ : protPropCommit_);
954 PopOutgoingRequest pop(
955 outgoingRequests_, protPropTid_, OutgoingRequest(kind, member, false));
956 getWriter()->sendDirectRequest(
957 protPropTid_, protPropOid_, protPropType_, member, inArguments);
958 pop.clear();
961 void Bridge::makeReleaseCall(
962 OUString const & oid, css::uno::TypeDescription const & type)
964 AttachThread att(getThreadPool());
965 sendRequest(
966 att.getTid(), oid, type,
967 css::uno::TypeDescription("com.sun.star.uno.XInterface::release"),
968 std::vector< BinaryAny >());
971 void Bridge::sendRequest(
972 rtl::ByteSequence const & tid, OUString const & oid,
973 css::uno::TypeDescription const & type,
974 css::uno::TypeDescription const & member,
975 std::vector< BinaryAny > const & inArguments)
977 getWriter()->queueRequest(tid, oid, type, member, inArguments);
980 void Bridge::throwException(bool exception, BinaryAny const & value) {
981 if (exception) {
982 cppu::throwException(mapBinaryToCppAny(value));
986 css::uno::Any Bridge::mapBinaryToCppAny(BinaryAny const & binaryAny) {
987 BinaryAny in(binaryAny);
988 css::uno::Any out;
989 out.~Any();
990 uno_copyAndConvertData(
991 &out, &in.get(),
992 css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(),
993 binaryToCppMapping_.get());
994 return out;
997 bool Bridge::becameUnused() const {
998 return stubs_.empty() && proxies_ == 0 && calls_ == 0 && normalCall_;
1001 void Bridge::terminateWhenUnused(bool unused) {
1002 if (unused) {
1003 // That the current thread considers the bridge unused implies that it
1004 // is not within an incoming or outgoing remote call (so calling
1005 // terminate cannot lead to deadlock):
1006 terminate(false);
1010 void Bridge::checkDisposed() {
1011 assert(state_ != STATE_INITIAL);
1012 if (state_ != STATE_STARTED) {
1013 throw css::lang::DisposedException(
1014 "Binary URP bridge already disposed",
1015 static_cast< cppu::OWeakObject * >(this));
1021 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */