lok: jsbuilder: avoid sending early id to client side
[LibreOffice.git] / binaryurp / source / bridge.cxx
blobfccef42d98f4da8585b047de1e6a98f98e387869
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/string.h>
50 #include <rtl/ustring.hxx>
51 #include <sal/log.hxx>
52 #include <sal/types.h>
53 #include <typelib/typeclass.h>
54 #include <typelib/typedescription.h>
55 #include <typelib/typedescription.hxx>
56 #include <uno/dispatcher.hxx>
57 #include <uno/environment.hxx>
58 #include <uno/lbnames.h>
60 #include "binaryany.hxx"
61 #include "bridge.hxx"
62 #include "bridgefactory.hxx"
63 #include "incomingreply.hxx"
64 #include "lessoperators.hxx"
65 #include "outgoingrequest.hxx"
66 #include "outgoingrequests.hxx"
67 #include "proxy.hxx"
68 #include "reader.hxx"
70 namespace binaryurp {
72 namespace {
74 sal_Int32 random() {
75 sal_Int32 n;
76 rtlRandomPool pool = rtl_random_createPool();
77 rtl_random_getBytes(pool, &n, sizeof n);
78 rtl_random_destroyPool(pool);
79 return n;
82 OUString toString(css::uno::TypeDescription const & type) {
83 typelib_TypeDescription * d = type.get();
84 assert(d != nullptr && d->pTypeName != nullptr);
85 return OUString(d->pTypeName);
88 extern "C" void freeProxyCallback(
89 SAL_UNUSED_PARAMETER uno_ExtEnvironment *, void * pProxy)
91 assert(pProxy != nullptr);
92 static_cast< Proxy * >(pProxy)->do_free();
95 bool isThread(salhelper::Thread * thread) {
96 assert(thread != nullptr);
97 return osl::Thread::getCurrentIdentifier() == thread->getIdentifier();
100 class AttachThread {
101 public:
102 explicit AttachThread(uno_ThreadPool threadPool);
104 ~AttachThread();
106 const rtl::ByteSequence& getTid() const throw () { return tid_;}
108 private:
109 AttachThread(const AttachThread&) = delete;
110 AttachThread& operator=(const AttachThread&) = delete;
112 uno_ThreadPool threadPool_;
113 rtl::ByteSequence tid_;
116 AttachThread::AttachThread(uno_ThreadPool threadPool): threadPool_(threadPool) {
117 sal_Sequence * s = nullptr;
118 uno_getIdOfCurrentThread(&s);
119 tid_ = rtl::ByteSequence(s, rtl::BYTESEQ_NOACQUIRE);
120 uno_threadpool_attach(threadPool_);
123 AttachThread::~AttachThread() {
124 uno_threadpool_detach(threadPool_);
125 uno_releaseIdFromCurrentThread();
129 class PopOutgoingRequest {
130 public:
131 PopOutgoingRequest(
132 OutgoingRequests & requests, rtl::ByteSequence const & tid,
133 OutgoingRequest const & request);
135 ~PopOutgoingRequest();
137 void clear();
139 private:
140 PopOutgoingRequest(const PopOutgoingRequest&) = delete;
141 PopOutgoingRequest& operator=(const PopOutgoingRequest&) = delete;
143 OutgoingRequests & requests_;
144 rtl::ByteSequence tid_;
145 bool cleared_;
148 PopOutgoingRequest::PopOutgoingRequest(
149 OutgoingRequests & requests, rtl::ByteSequence const & tid,
150 OutgoingRequest const & request):
151 requests_(requests), tid_(tid), cleared_(false)
153 requests_.push(tid_, request);
156 PopOutgoingRequest::~PopOutgoingRequest() {
157 if (!cleared_) {
158 requests_.pop(tid_);
162 void PopOutgoingRequest::clear() {
163 cleared_ = true;
168 struct Bridge::SubStub {
169 com::sun::star::uno::UnoInterfaceReference object;
171 sal_uInt32 references;
174 Bridge::Bridge(
175 rtl::Reference< BridgeFactory > const & factory, 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_(UNO_LB_UNO),
181 cppToBinaryMapping_(CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO),
182 binaryToCppMapping_(UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME),
183 protPropTid_(
184 reinterpret_cast< sal_Int8 const * >(".UrpProtocolPropertiesTid"),
185 RTL_CONSTASCII_LENGTH(".UrpProtocolPropertiesTid")),
186 protPropOid_("UrpProtocolProperties"),
187 protPropType_(
188 cppu::UnoType<
189 css::uno::Reference< css::bridge::XProtocolProperties > >::get()),
190 protPropRequest_("com.sun.star.bridge.XProtocolProperties::requestChange"),
191 protPropCommit_("com.sun.star.bridge.XProtocolProperties::commitChange"),
192 state_(STATE_INITIAL), threadPool_(nullptr), currentContextMode_(false),
193 proxies_(0), calls_(0), normalCall_(false), activeCalls_(0),
194 mode_(MODE_REQUESTED)
196 assert(factory.is() && connection.is());
197 if (!binaryUno_.is()) {
198 throw css::uno::RuntimeException("URP: no binary UNO environment");
200 if (!(cppToBinaryMapping_.is() && binaryToCppMapping_.is())) {
201 throw css::uno::RuntimeException("URP: no C++ UNO mapping");
203 passive_.set();
206 void Bridge::start() {
207 rtl::Reference r(new Reader(this));
208 rtl::Reference w(new Writer(this));
210 osl::MutexGuard g(mutex_);
211 assert(
212 state_ == STATE_INITIAL && threadPool_ == nullptr && !writer_.is() &&
213 !reader_.is());
214 threadPool_ = uno_threadpool_create();
215 assert(threadPool_ != nullptr);
216 reader_ = r;
217 writer_ = w;
218 state_ = STATE_STARTED;
220 // It is important to call reader_->launch() last here; both
221 // Writer::execute and Reader::execute can call Bridge::terminate, but
222 // Writer::execute is initially blocked in unblocked_.wait() until
223 // Reader::execute has called bridge_->sendRequestChangeRequest(), so
224 // effectively only reader_->launch() can lead to an early call to
225 // Bridge::terminate
226 w->launch();
227 r->launch();
230 void Bridge::terminate(bool final) {
231 uno_ThreadPool tp;
232 // Make sure function-local variables (Stubs s, etc.) are destroyed before
233 // the final uno_threadpool_destroy/threadPool_ = 0:
235 rtl::Reference< Reader > r;
236 rtl::Reference< Writer > w;
237 bool joinW;
238 Listeners ls;
240 osl::ClearableMutexGuard g(mutex_);
241 switch (state_) {
242 case STATE_INITIAL: // via ~Bridge -> dispose -> terminate
243 case STATE_FINAL:
244 return;
245 case STATE_STARTED:
246 break;
247 case STATE_TERMINATED:
248 if (final) {
249 g.clear();
250 terminated_.wait();
252 osl::MutexGuard g2(mutex_);
253 tp = threadPool_;
254 threadPool_ = nullptr;
255 if (reader_.is()) {
256 if (!isThread(reader_.get())) {
257 r = reader_;
259 reader_.clear();
261 if (writer_.is()) {
262 if (!isThread(writer_.get())) {
263 w = writer_;
265 writer_.clear();
267 state_ = STATE_FINAL;
269 assert(!(r.is() && w.is()));
270 if (r.is()) {
271 r->join();
272 } else if (w.is()) {
273 w->join();
275 if (tp != nullptr) {
276 uno_threadpool_destroy(tp);
279 return;
281 tp = threadPool_;
282 assert(!(final && isThread(reader_.get())));
283 if (!isThread(reader_.get())) {
284 std::swap(reader_, r);
286 w = writer_;
287 joinW = !isThread(writer_.get());
288 assert(!final || joinW);
289 if (joinW) {
290 writer_.clear();
292 ls.swap(listeners_);
293 state_ = final ? STATE_FINAL : STATE_TERMINATED;
295 try {
296 connection_->close();
297 } catch (const css::io::IOException & e) {
298 SAL_INFO("binaryurp", "caught IO exception '" << e << '\'');
300 assert(w.is());
301 w->stop();
302 if (r.is()) {
303 r->join();
305 if (joinW) {
306 w->join();
308 assert(tp != nullptr);
309 uno_threadpool_dispose(tp);
310 Stubs s;
312 osl::MutexGuard g(mutex_);
313 s.swap(stubs_);
315 for (auto & stub : s)
317 for (auto & item : stub.second)
319 SAL_INFO(
320 "binaryurp",
321 "stub '" << stub.first << "', '" << toString(item.first)
322 << "' still mapped at Bridge::terminate");
323 binaryUno_.get()->pExtEnv->revokeInterface(
324 binaryUno_.get()->pExtEnv, item.second.object.get());
327 factory_->removeBridge(this);
328 for (auto const& listener : ls)
330 try {
331 listener->disposing(
332 css::lang::EventObject(
333 static_cast< cppu::OWeakObject * >(this)));
334 } catch (const css::uno::RuntimeException & e) {
335 SAL_WARN("binaryurp", "caught " << e);
339 if (final) {
340 uno_threadpool_destroy(tp);
343 osl::MutexGuard g(mutex_);
344 if (final) {
345 threadPool_ = nullptr;
348 terminated_.set();
352 BinaryAny Bridge::mapCppToBinaryAny(css::uno::Any const & cppAny) {
353 css::uno::Any in(cppAny);
354 BinaryAny out;
355 out.~BinaryAny();
356 uno_copyAndConvertData(
357 &out.get(), &in,
358 css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(),
359 cppToBinaryMapping_.get());
360 return out;
363 uno_ThreadPool Bridge::getThreadPool() {
364 osl::MutexGuard g(mutex_);
365 checkDisposed();
366 assert(threadPool_ != nullptr);
367 return threadPool_;
370 rtl::Reference< Writer > Bridge::getWriter() {
371 osl::MutexGuard g(mutex_);
372 checkDisposed();
373 assert(writer_.is());
374 return writer_;
377 css::uno::UnoInterfaceReference Bridge::registerIncomingInterface(
378 OUString const & oid, css::uno::TypeDescription const & type)
380 assert(type.is());
381 if (oid.isEmpty()) {
382 return css::uno::UnoInterfaceReference();
384 css::uno::UnoInterfaceReference obj(findStub(oid, type));
385 if (!obj.is()) {
386 binaryUno_.get()->pExtEnv->getRegisteredInterface(
387 binaryUno_.get()->pExtEnv,
388 reinterpret_cast< void ** >(&obj.m_pUnoI), oid.pData,
389 reinterpret_cast< typelib_InterfaceTypeDescription * >(type.get()));
390 if (obj.is()) {
391 makeReleaseCall(oid, type);
392 } else {
393 obj.set(new Proxy(this, oid, type), SAL_NO_ACQUIRE);
395 osl::MutexGuard g(mutex_);
396 assert(proxies_ < std::numeric_limits< std::size_t >::max());
397 ++proxies_;
399 binaryUno_.get()->pExtEnv->registerProxyInterface(
400 binaryUno_.get()->pExtEnv,
401 reinterpret_cast< void ** >(&obj.m_pUnoI), &freeProxyCallback,
402 oid.pData,
403 reinterpret_cast< typelib_InterfaceTypeDescription * >(
404 type.get()));
407 return obj;
410 OUString Bridge::registerOutgoingInterface(
411 css::uno::UnoInterfaceReference const & object,
412 css::uno::TypeDescription const & type)
414 assert(type.is());
415 if (!object.is()) {
416 return OUString();
418 OUString oid;
419 if (!Proxy::isProxy(this, object, &oid)) {
420 binaryUno_.get()->pExtEnv->getObjectIdentifier(
421 binaryUno_.get()->pExtEnv, &oid.pData, object.get());
422 osl::MutexGuard g(mutex_);
423 Stubs::iterator i(stubs_.find(oid));
424 Stub newStub;
425 Stub * stub = i == stubs_.end() ? &newStub : &i->second;
426 Stub::iterator j(stub->find(type));
427 //TODO: Release sub-stub if it is not successfully sent to remote side
428 // (otherwise, stub will leak until terminate()):
429 if (j == stub->end()) {
430 j = stub->emplace(type, SubStub()).first;
431 if (stub == &newStub) {
432 i = stubs_.emplace(oid, Stub()).first;
433 std::swap(i->second, newStub);
434 j = i->second.find(type);
435 assert(j != i->second.end());
437 j->second.object = object;
438 j->second.references = 1;
439 binaryUno_.get()->pExtEnv->registerInterface(
440 binaryUno_.get()->pExtEnv,
441 reinterpret_cast< void ** >(&j->second.object.m_pUnoI),
442 oid.pData,
443 reinterpret_cast< typelib_InterfaceTypeDescription * >(
444 type.get()));
445 } else {
446 assert(stub != &newStub);
447 if (j->second.references == SAL_MAX_UINT32) {
448 throw css::uno::RuntimeException(
449 "URP: stub reference count overflow");
451 ++j->second.references;
454 return oid;
457 css::uno::UnoInterfaceReference Bridge::findStub(
458 OUString const & oid, css::uno::TypeDescription const & type)
460 assert(!oid.isEmpty() && type.is());
461 osl::MutexGuard g(mutex_);
462 Stubs::iterator i(stubs_.find(oid));
463 if (i != stubs_.end()) {
464 Stub::iterator j(i->second.find(type));
465 if (j != i->second.end()) {
466 return j->second.object;
468 for (auto const& item : i->second)
470 if (typelib_typedescription_isAssignableFrom(
471 type.get(), item.first.get()))
473 return item.second.object;
477 return css::uno::UnoInterfaceReference();
480 void Bridge::releaseStub(
481 OUString const & oid, css::uno::TypeDescription const & type)
483 assert(!oid.isEmpty() && type.is());
484 css::uno::UnoInterfaceReference obj;
485 bool unused;
487 osl::MutexGuard g(mutex_);
488 Stubs::iterator i(stubs_.find(oid));
489 if (i == stubs_.end()) {
490 throw css::uno::RuntimeException("URP: release unknown stub");
492 Stub::iterator j(i->second.find(type));
493 if (j == i->second.end()) {
494 throw css::uno::RuntimeException("URP: release unknown stub");
496 assert(j->second.references > 0);
497 --j->second.references;
498 if (j->second.references == 0) {
499 obj = j->second.object;
500 i->second.erase(j);
501 if (i->second.empty()) {
502 stubs_.erase(i);
505 unused = becameUnused();
507 if (obj.is()) {
508 binaryUno_.get()->pExtEnv->revokeInterface(
509 binaryUno_.get()->pExtEnv, obj.get());
511 terminateWhenUnused(unused);
514 void Bridge::resurrectProxy(Proxy & proxy) {
515 uno_Interface * p = &proxy;
516 binaryUno_.get()->pExtEnv->registerProxyInterface(
517 binaryUno_.get()->pExtEnv,
518 reinterpret_cast< void ** >(&p), &freeProxyCallback,
519 proxy.getOid().pData,
520 reinterpret_cast< typelib_InterfaceTypeDescription * >(
521 proxy.getType().get()));
522 assert(p == &proxy);
525 void Bridge::revokeProxy(Proxy & proxy) {
526 binaryUno_.get()->pExtEnv->revokeInterface(
527 binaryUno_.get()->pExtEnv, &proxy);
530 void Bridge::freeProxy(Proxy & proxy) {
531 try {
532 makeReleaseCall(proxy.getOid(), proxy.getType());
533 } catch (const css::uno::RuntimeException & e) {
534 SAL_INFO(
535 "binaryurp", "caught runtime exception '" << e << '\'');
536 } catch (const std::exception & e) {
537 SAL_WARN("binaryurp", "caught C++ exception '" << e.what() << '\'');
539 bool unused;
541 osl::MutexGuard g(mutex_);
542 assert(proxies_ > 0);
543 --proxies_;
544 unused = becameUnused();
546 terminateWhenUnused(unused);
549 void Bridge::incrementCalls(bool normalCall) throw () {
550 osl::MutexGuard g(mutex_);
551 assert(calls_ < std::numeric_limits< std::size_t >::max());
552 ++calls_;
553 normalCall_ |= normalCall;
556 void Bridge::decrementCalls() {
557 bool unused;
559 osl::MutexGuard g(mutex_);
560 assert(calls_ > 0);
561 --calls_;
562 unused = becameUnused();
564 terminateWhenUnused(unused);
567 void Bridge::incrementActiveCalls() throw () {
568 osl::MutexGuard g(mutex_);
569 assert(
570 activeCalls_ <= calls_ &&
571 activeCalls_ < std::numeric_limits< std::size_t >::max());
572 ++activeCalls_;
573 passive_.reset();
576 void Bridge::decrementActiveCalls() throw () {
577 osl::MutexGuard g(mutex_);
578 assert(activeCalls_ <= calls_ && activeCalls_ > 0);
579 --activeCalls_;
580 if (activeCalls_ == 0) {
581 passive_.set();
585 bool Bridge::makeCall(
586 OUString const & oid, css::uno::TypeDescription const & member,
587 bool setter, std::vector< BinaryAny > const & inArguments,
588 BinaryAny * returnValue, std::vector< BinaryAny > * outArguments)
590 std::unique_ptr< IncomingReply > resp;
592 uno_ThreadPool tp = getThreadPool();
593 AttachThread att(tp);
594 PopOutgoingRequest pop(
595 outgoingRequests_, att.getTid(),
596 OutgoingRequest(OutgoingRequest::KIND_NORMAL, member, setter));
597 sendRequest(
598 att.getTid(), oid, css::uno::TypeDescription(), member,
599 inArguments);
600 pop.clear();
601 incrementCalls(true);
602 incrementActiveCalls();
603 void * job;
604 uno_threadpool_enter(tp, &job);
605 resp.reset(static_cast< IncomingReply * >(job));
606 decrementActiveCalls();
607 decrementCalls();
609 if (resp == nullptr)
611 throw css::lang::DisposedException(
612 "Binary URP bridge disposed during call",
613 static_cast< cppu::OWeakObject * >(this));
615 *returnValue = resp->returnValue;
616 if (!resp->exception) {
617 *outArguments = resp->outArguments;
619 return resp->exception;
622 void Bridge::sendRequestChangeRequest() {
623 assert(mode_ == MODE_REQUESTED);
624 random_ = random();
625 std::vector< BinaryAny > a;
626 a.emplace_back(
627 css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()),
628 &random_);
629 sendProtPropRequest(OutgoingRequest::KIND_REQUEST_CHANGE, a);
632 void Bridge::handleRequestChangeReply(
633 bool exception, BinaryAny const & returnValue)
635 try {
636 throwException(exception, returnValue);
637 } catch (css::uno::RuntimeException & e) {
638 // Before OOo 2.2, Java URP would throw a RuntimeException when
639 // receiving a requestChange message (see i#35277 "Java URP: Support
640 // Manipulation of Protocol Properties"):
641 if (mode_ != MODE_REQUESTED) {
642 throw;
644 SAL_WARN(
645 "binaryurp",
646 "requestChange caught " << e << " in state 'requested'");
647 mode_ = MODE_NORMAL;
648 getWriter()->unblock();
649 decrementCalls();
650 return;
652 sal_Int32 n = *static_cast< sal_Int32 * >(
653 returnValue.getValue(
654 css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get())));
655 sal_Int32 exp = 0;
656 switch (mode_) {
657 case MODE_REQUESTED:
658 case MODE_REPLY_1:
659 exp = 1;
660 break;
661 case MODE_REPLY_MINUS1:
662 exp = -1;
663 mode_ = MODE_REQUESTED;
664 break;
665 case MODE_REPLY_0:
666 exp = 0;
667 mode_ = MODE_WAIT;
668 break;
669 default:
670 assert(false); // this cannot happen
671 break;
673 if (n != exp) {
674 throw css::uno::RuntimeException(
675 "URP: requestChange reply with unexpected return value received",
676 static_cast< cppu::OWeakObject * >(this));
678 decrementCalls();
679 switch (exp) {
680 case -1:
681 sendRequestChangeRequest();
682 break;
683 case 0:
684 break;
685 case 1:
686 sendCommitChangeRequest();
687 break;
688 default:
689 assert(false); // this cannot happen
690 break;
694 void Bridge::handleCommitChangeReply(
695 bool exception, BinaryAny const & returnValue)
697 bool bCcMode = true;
698 try {
699 throwException(exception, returnValue);
700 } catch (const css::bridge::InvalidProtocolChangeException &) {
701 bCcMode = false;
703 if (bCcMode) {
704 setCurrentContextMode();
706 assert(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1);
707 mode_ = MODE_NORMAL;
708 getWriter()->unblock();
709 decrementCalls();
712 void Bridge::handleRequestChangeRequest(
713 rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments)
715 assert(inArguments.size() == 1);
716 switch (mode_) {
717 case MODE_REQUESTED:
719 sal_Int32 n2 = *static_cast< sal_Int32 * >(
720 inArguments[0].getValue(
721 css::uno::TypeDescription(
722 cppu::UnoType< sal_Int32 >::get())));
723 sal_Int32 ret;
724 if (n2 > random_) {
725 ret = 1;
726 mode_ = MODE_REPLY_0;
727 } else if (n2 == random_) {
728 ret = -1;
729 mode_ = MODE_REPLY_MINUS1;
730 } else {
731 ret = 0;
732 mode_ = MODE_REPLY_1;
734 getWriter()->sendDirectReply(
735 tid, protPropRequest_, false,
736 BinaryAny(
737 css::uno::TypeDescription(
738 cppu::UnoType< sal_Int32 >::get()),
739 &ret),
740 std::vector< BinaryAny >());
741 break;
743 case MODE_NORMAL:
745 mode_ = MODE_NORMAL_WAIT;
746 sal_Int32 ret = 1;
747 getWriter()->queueReply(
748 tid, protPropRequest_, false, false,
749 BinaryAny(
750 css::uno::TypeDescription(
751 cppu::UnoType< sal_Int32 >::get()),
752 &ret),
753 std::vector< BinaryAny >(), false);
754 break;
756 default:
757 throw css::uno::RuntimeException(
758 "URP: unexpected requestChange request received",
759 static_cast< cppu::OWeakObject * >(this));
763 void Bridge::handleCommitChangeRequest(
764 rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments)
766 bool bCcMode = false;
767 bool bExc = false;
768 BinaryAny ret;
769 assert(inArguments.size() == 1);
770 css::uno::Sequence< css::bridge::ProtocolProperty > s;
771 [[maybe_unused]] bool ok = (mapBinaryToCppAny(inArguments[0]) >>= s);
772 assert(ok);
773 for (const auto & pp : std::as_const(s)) {
774 if (pp.Name == "CurrentContext") {
775 bCcMode = true;
776 } else {
777 bCcMode = false;
778 bExc = true;
779 ret = mapCppToBinaryAny(
780 css::uno::Any(
781 css::bridge::InvalidProtocolChangeException(
782 "InvalidProtocolChangeException",
783 css::uno::Reference< css::uno::XInterface >(), pp,
784 1)));
785 break;
788 switch (mode_) {
789 case MODE_WAIT:
790 getWriter()->sendDirectReply(
791 tid, protPropCommit_, bExc, ret, std::vector< BinaryAny >());
792 if (bCcMode) {
793 setCurrentContextMode();
794 mode_ = MODE_NORMAL;
795 getWriter()->unblock();
796 } else {
797 mode_ = MODE_REQUESTED;
798 sendRequestChangeRequest();
800 break;
801 case MODE_NORMAL_WAIT:
802 getWriter()->queueReply(
803 tid, protPropCommit_, false, false, ret, std::vector< BinaryAny >(),
804 bCcMode);
805 mode_ = MODE_NORMAL;
806 break;
807 default:
808 throw css::uno::RuntimeException(
809 "URP: unexpected commitChange request received",
810 static_cast< cppu::OWeakObject * >(this));
814 OutgoingRequest Bridge::lastOutgoingRequest(rtl::ByteSequence const & tid) {
815 OutgoingRequest req(outgoingRequests_.top(tid));
816 outgoingRequests_.pop(tid);
817 return req;
820 bool Bridge::isProtocolPropertiesRequest(
821 std::u16string_view oid, css::uno::TypeDescription const & type) const
823 return oid == protPropOid_ && type.equals(protPropType_);
826 void Bridge::setCurrentContextMode() {
827 osl::MutexGuard g(mutex_);
828 currentContextMode_ = true;
831 bool Bridge::isCurrentContextMode() {
832 osl::MutexGuard g(mutex_);
833 return currentContextMode_;
836 Bridge::~Bridge() {
837 #if OSL_DEBUG_LEVEL > 0
839 osl::MutexGuard g(mutex_);
840 SAL_WARN_IF(
841 state_ == STATE_STARTED || state_ == STATE_TERMINATED, "binaryurp",
842 "undisposed bridge \"" << name_ <<"\" in state " << state_
843 << ", 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 auto const t = ret.getType();
879 if (t.get()->eTypeClass == typelib_TypeClass_VOID) {
880 return {};
882 if (!t.equals(ifc)) {
883 throw css::uno::RuntimeException(
884 "initial object queryInterface for OID \"" + sInstanceName + "\" returned ANY of type "
885 + OUString::unacquired(&t.get()->pTypeName));
887 auto const val = *static_cast< uno_Interface ** >(ret.getValue(ifc));
888 if (val == nullptr) {
889 throw css::uno::RuntimeException(
890 "initial object queryInterface for OID \"" + sInstanceName
891 + "\" returned null css.uno.XInterface ANY");
893 return css::uno::Reference< css::uno::XInterface >(
894 static_cast< css::uno::XInterface * >(
895 binaryToCppMapping_.mapInterface(
896 val,
897 ifc.get())),
898 SAL_NO_ACQUIRE);
901 OUString Bridge::getName() {
902 return name_;
905 OUString Bridge::getDescription() {
906 OUString b = name_ + ":" + connection_->getDescription();
907 return b;
910 void Bridge::dispose() {
911 // For terminate(true) not to deadlock, an external protocol must ensure
912 // that dispose is not called from a thread pool worker thread (that dispose
913 // is never called from the reader or writer thread is already ensured
914 // internally):
915 terminate(true);
916 // OOo expects dispose to not return while there are still remote calls in
917 // progress; an external protocol must ensure that dispose is not called
918 // from within an incoming or outgoing remote call, as passive_.wait() would
919 // otherwise deadlock:
920 passive_.wait();
923 void Bridge::addEventListener(
924 css::uno::Reference< css::lang::XEventListener > const & xListener)
926 assert(xListener.is());
928 osl::MutexGuard g(mutex_);
929 assert(state_ != STATE_INITIAL);
930 if (state_ == STATE_STARTED) {
931 listeners_.push_back(xListener);
932 return;
935 xListener->disposing(
936 css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
939 void Bridge::removeEventListener(
940 css::uno::Reference< css::lang::XEventListener > const & aListener)
942 osl::MutexGuard g(mutex_);
943 Listeners::iterator i(
944 std::find(listeners_.begin(), listeners_.end(), aListener));
945 if (i != listeners_.end()) {
946 listeners_.erase(i);
950 void Bridge::sendCommitChangeRequest() {
951 assert(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1);
952 css::uno::Sequence< css::bridge::ProtocolProperty > s(1);
953 s[0].Name = "CurrentContext";
954 std::vector< BinaryAny > a;
955 a.push_back(mapCppToBinaryAny(css::uno::Any(s)));
956 sendProtPropRequest(OutgoingRequest::KIND_COMMIT_CHANGE, a);
959 void Bridge::sendProtPropRequest(
960 OutgoingRequest::Kind kind, std::vector< BinaryAny > const & inArguments)
962 assert(
963 kind == OutgoingRequest::KIND_REQUEST_CHANGE ||
964 kind == OutgoingRequest::KIND_COMMIT_CHANGE);
965 incrementCalls(false);
966 css::uno::TypeDescription member(
967 kind == OutgoingRequest::KIND_REQUEST_CHANGE
968 ? protPropRequest_ : protPropCommit_);
969 PopOutgoingRequest pop(
970 outgoingRequests_, protPropTid_, OutgoingRequest(kind, member, false));
971 getWriter()->sendDirectRequest(
972 protPropTid_, protPropOid_, protPropType_, member, inArguments);
973 pop.clear();
976 void Bridge::makeReleaseCall(
977 OUString const & oid, css::uno::TypeDescription const & type)
979 //HACK to decouple the processing of release calls from all other threads. Normally, sending
980 // the release request should use the current thread's TID (via AttachThread), which would cause
981 // that asynchronous request to be processed by a physical thread that is paired with the
982 // physical thread processing the normal synchronous call stack (see ThreadIdHashMap in
983 // cppu/source/threadpool/threadpool.hxx). However, that can lead to deadlock when a thread
984 // illegally makes a synchronous UNO call with the SolarMutex locked (e.g.,
985 // SfxBaseModel::postEvent_Impl in sfx2/source/doc/sfxbasemodel.cxx doing documentEventOccurred
986 // and notifyEvent calls), and while that call is on the stack the remote side sends back some
987 // release request on the same logical UNO thread for an object that wants to acquire the
988 // SolarMutex in its destructor (e.g., SwXTextDocument in sw/inc/unotxdoc.hxx holding its
989 // m_pImpl via an sw::UnoImplPtr). While the correct approach would be to not make UNO calls
990 // with the SolarMutex (or any other mutex) locked, fixing that would probably be a heroic
991 // effort. So for now live with this hack, hoping that it does not introduce any new issues of
992 // its own:
993 static auto const tid = [] {
994 static sal_Int8 const id[] = {'r', 'e', 'l', 'e', 'a', 's', 'e', 'h', 'a', 'c', 'k'};
995 return rtl::ByteSequence(id, SAL_N_ELEMENTS(id));
996 }();
997 sendRequest(
998 tid, oid, type,
999 css::uno::TypeDescription("com.sun.star.uno.XInterface::release"),
1000 std::vector< BinaryAny >());
1003 void Bridge::sendRequest(
1004 rtl::ByteSequence const & tid, OUString const & oid,
1005 css::uno::TypeDescription const & type,
1006 css::uno::TypeDescription const & member,
1007 std::vector< BinaryAny > const & inArguments)
1009 getWriter()->queueRequest(tid, oid, type, member, inArguments);
1012 void Bridge::throwException(bool exception, BinaryAny const & value) {
1013 if (exception) {
1014 cppu::throwException(mapBinaryToCppAny(value));
1018 css::uno::Any Bridge::mapBinaryToCppAny(BinaryAny const & binaryAny) {
1019 BinaryAny in(binaryAny);
1020 css::uno::Any out;
1021 out.~Any();
1022 uno_copyAndConvertData(
1023 &out, &in.get(),
1024 css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(),
1025 binaryToCppMapping_.get());
1026 return out;
1029 bool Bridge::becameUnused() const {
1030 return stubs_.empty() && proxies_ == 0 && calls_ == 0 && normalCall_;
1033 void Bridge::terminateWhenUnused(bool unused) {
1034 if (unused) {
1035 // That the current thread considers the bridge unused implies that it
1036 // is not within an incoming or outgoing remote call (so calling
1037 // terminate cannot lead to deadlock):
1038 terminate(false);
1042 void Bridge::checkDisposed() {
1043 assert(state_ != STATE_INITIAL);
1044 if (state_ != STATE_STARTED) {
1045 throw css::lang::DisposedException(
1046 "Binary URP bridge already disposed",
1047 static_cast< cppu::OWeakObject * >(this));
1053 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */