From b1b3bd819157fa0a438b17966ecfb1c09dba7d79 Mon Sep 17 00:00:00 2001
From: Markus Himmel <markus@himmel-villmar.de>
Date: Sun, 8 Nov 2015 15:16:05 +0000
Subject: [PATCH 2/2] Make sure images containing BTranslators are not unloaded
early
When a translator is uninstalled, BTranslatorPrivate::_RemoveTranslators is
called. This method used to unload the image containing the translator after
calling Release() on it resulting in several problems:
- If the translator was still busy, e.g. translating something while being
installed, it crashed since the image was unloaded even though its refcount
was larger than 0.
- Applications using code from one of the translators (e.g. its config view)
would crash when the translator is uninstalled (this is bug #12005).
This problem is now fixed. The roster keeps track of all translators whose
image it manages (even if the translator was already removed from the roster).
It also keeps a refcount to all images. When a translator's refcount drops to
zero and it belonged to a roster at some point, it does not delete itself, but
notifies the roster that it is ready to destruct, which then removes it from
the roster if the translator is still in it, destroys the translator, decrements
the refcount of the image and if the new refcount is zero, unloads the image.
All of this is done in a message handler, since if the translator called
TranslatorDeleted like before, the unloaded image would be referenced when
the stack is walked up.
Finally, the DataTranslations preflet is required to Acquire() the translator
whose config view it is showing, because otherwise its refcount could be reduced
to 0 and the image unloaded. BTranslatorRoster now enables users to acquire a
translator by ID. By the time the translator has to be released, it might not
be part of the roster anymore though. Since BTranslatorRoster tries not to give
out raw pointers to the translators it manages, users who acquire a translator
through a roster now are given a BTranslatorReleaseDelegate, which allows for
releasing the BTranslator exactly once and then self-destructs.
---
headers/os/app/AppDefs.h | 1 +
headers/os/translation/TranslatorRoster.h | 12 +++-
src/kits/translation/Translator.cpp | 21 ++++--
src/kits/translation/TranslatorRoster.cpp | 75 +++++++++++++++++-----
src/kits/translation/TranslatorRosterPrivate.h | 9 ++-
.../datatranslations/DataTranslationsWindow.cpp | 16 ++++-
.../datatranslations/DataTranslationsWindow.h | 3 +
7 files changed, 111 insertions(+), 26 deletions(-)
diff --git a/headers/os/app/AppDefs.h b/headers/os/app/AppDefs.h
index 77383ef..0a70d8b 100644
a
|
b
|
enum {
|
59 | 59 | B_VALUE_CHANGED = '_VCH', |
60 | 60 | B_TRANSLATOR_ADDED = '_ART', |
61 | 61 | B_TRANSLATOR_REMOVED = '_RRT', |
| 62 | B_DELETE_TRANSLATOR = '_DRT', |
62 | 63 | B_VIEW_MOVED = '_VMV', |
63 | 64 | B_VIEW_RESIZED = '_VRS', |
64 | 65 | B_WINDOW_MOVED = '_WMV', |
diff --git a/headers/os/translation/TranslatorRoster.h b/headers/os/translation/TranslatorRoster.h
index 4d95853..06f31ff 100644
a
|
b
|
|
9 | 9 | #include <Archivable.h> |
10 | 10 | #include <TranslationDefs.h> |
11 | 11 | |
12 | | |
13 | 12 | struct translation_format; |
14 | 13 | |
15 | 14 | class BBitmap; |
… |
… |
class BTranslator;
|
22 | 21 | class BView; |
23 | 22 | struct entry_ref; |
24 | 23 | |
| 24 | class BTranslatorReleaseDelegate { |
| 25 | public: |
| 26 | BTranslatorReleaseDelegate(BTranslator* it); |
| 27 | |
| 28 | void Release(); |
| 29 | |
| 30 | private: |
| 31 | BTranslator* fUnderlying; |
| 32 | }; |
25 | 33 | |
26 | 34 | class BTranslatorRoster : public BArchivable { |
27 | 35 | public: |
… |
… |
public:
|
86 | 94 | translator_id translatorID, |
87 | 95 | BMessage* ioExtension); |
88 | 96 | |
| 97 | BTranslatorReleaseDelegate* AcquireTranslator(int32 translatorID); |
| 98 | |
89 | 99 | status_t GetRefFor(translator_id translatorID, |
90 | 100 | entry_ref* ref); |
91 | 101 | bool IsTranslator(entry_ref* ref); |
diff --git a/src/kits/translation/Translator.cpp b/src/kits/translation/Translator.cpp
index 5699901..33e91ab 100644
a
|
b
|
BTranslator::BTranslator()
|
24 | 24 | |
25 | 25 | BTranslator::~BTranslator() |
26 | 26 | { |
27 | | if (fOwningRoster != NULL) |
28 | | fOwningRoster->TranslatorDeleted(fID); |
29 | 27 | } |
30 | 28 | |
31 | 29 | |
… |
… |
BTranslator *BTranslator::Acquire()
|
52 | 50 | BTranslator *BTranslator::Release() |
53 | 51 | { |
54 | 52 | int32 oldValue = atomic_add(&fRefCount, -1); |
55 | | if (oldValue > 0) |
| 53 | if (oldValue > 1) |
56 | 54 | return this; |
57 | 55 | |
58 | | delete this; |
| 56 | if (fOwningRoster == NULL) { |
| 57 | delete this; |
| 58 | return NULL; |
| 59 | } |
| 60 | |
| 61 | // If we have ever been part of a roster, notify the roster to delete us |
| 62 | // and unload our image in a thread-safe way |
| 63 | BMessage del(B_DELETE_TRANSLATOR); |
| 64 | |
| 65 | del.AddPointer("ptr", this); |
| 66 | del.AddInt32("id", fID); |
| 67 | |
| 68 | BMessenger sender(fOwningRoster); |
| 69 | sender.SendMessage(&del); |
| 70 | |
59 | 71 | return NULL; |
60 | 72 | } |
61 | 73 | |
62 | | |
63 | 74 | int32 |
64 | 75 | BTranslator::ReferenceCount() |
65 | 76 | { |
diff --git a/src/kits/translation/TranslatorRoster.cpp b/src/kits/translation/TranslatorRoster.cpp
index a0b54af..12a5724 100644
a
|
b
|
BTranslatorRoster::Private::~Private()
|
193 | 193 | while (iterator != fTranslators.end()) { |
194 | 194 | BTranslator* translator = iterator->second.translator; |
195 | 195 | |
196 | | translator->fOwningRoster = NULL; |
197 | | // we don't want to be notified about this anymore |
198 | | |
199 | 196 | images.insert(iterator->second.image); |
200 | 197 | translator->Release(); |
201 | 198 | |
… |
… |
BTranslatorRoster::Private::MessageReceived(BMessage* message)
|
318 | 315 | break; |
319 | 316 | } |
320 | 317 | |
| 318 | case B_DELETE_TRANSLATOR: |
| 319 | { |
| 320 | // A translator's refcount has been reduced to zero and it wants |
| 321 | // us to delete it. |
| 322 | int32 id; |
| 323 | void* self; |
| 324 | if (message->FindInt32("id", &id) == B_OK && |
| 325 | message->FindPointer("ptr", &self) == B_OK) { |
| 326 | _TranslatorDeleted(id, (BTranslator*)self); |
| 327 | } |
| 328 | break; |
| 329 | } |
| 330 | |
321 | 331 | default: |
322 | 332 | BHandler::MessageReceived(message); |
323 | 333 | break; |
… |
… |
BTranslatorRoster::Private::CreateTranslators(const entry_ref& ref,
|
592 | 602 | if (AddTranslator(translator, image, &ref, nodeRef.node) == B_OK) { |
593 | 603 | if (update) |
594 | 604 | update->AddInt32("translator_id", translator->fID); |
| 605 | fImageOrigins.insert(std::make_pair(translator, image)); |
595 | 606 | count++; |
596 | 607 | created++; |
597 | 608 | } else { |
… |
… |
BTranslatorRoster::Private::CreateTranslators(const entry_ref& ref,
|
600 | 611 | } |
601 | 612 | } |
602 | 613 | |
603 | | if (created == 0) |
| 614 | if (created == 0) { |
604 | 615 | unload_add_on(image); |
| 616 | } else { |
| 617 | // Initial refcount for the image that was just loaded |
| 618 | fKnownImages.insert(std::make_pair(image, created)); |
| 619 | } |
605 | 620 | |
606 | 621 | quarantine.Remove(); |
607 | 622 | return B_OK; |
… |
… |
BTranslatorRoster::Private::GetRefFor(translator_id id, entry_ref& ref)
|
850 | 865 | |
851 | 866 | |
852 | 867 | void |
853 | | BTranslatorRoster::Private::TranslatorDeleted(translator_id id) |
| 868 | BTranslatorRoster::Private::_TranslatorDeleted(translator_id id, BTranslator* self) |
854 | 869 | { |
855 | 870 | BAutolock locker(this); |
856 | 871 | |
857 | 872 | TranslatorMap::iterator iterator = fTranslators.find(id); |
858 | | if (iterator == fTranslators.end()) |
859 | | return; |
| 873 | if (iterator != fTranslators.end()) |
| 874 | fTranslators.erase(iterator); |
860 | 875 | |
861 | | fTranslators.erase(iterator); |
862 | | } |
| 876 | image_id image = fImageOrigins[self]; |
863 | 877 | |
| 878 | delete self; |
| 879 | |
| 880 | int32 former = atomic_add(&fKnownImages[image], -1); |
| 881 | if (former == 1) |
| 882 | { |
| 883 | unload_add_on(image); |
| 884 | fImageOrigins.erase(self); |
| 885 | fKnownImages.erase(image); |
| 886 | } |
| 887 | } |
864 | 888 | |
865 | 889 | /*static*/ int |
866 | 890 | BTranslatorRoster::Private::_CompareSupport(const void* _a, const void* _b) |
… |
… |
BTranslatorRoster::Private::_RemoveTranslators(const node_ref* nodeRef,
|
1073 | 1097 | if ((ref != NULL && item.ref == *ref) |
1074 | 1098 | || (nodeRef != NULL && item.ref.device == nodeRef->device |
1075 | 1099 | && item.node == nodeRef->node)) { |
1076 | | item.translator->fOwningRoster = NULL; |
1077 | | // if the translator is busy, we don't want to be notified |
1078 | | // about the removal later on |
1079 | 1100 | item.translator->Release(); |
1080 | 1101 | image = item.image; |
1081 | 1102 | update.AddInt32("translator_id", iterator->first); |
… |
… |
BTranslatorRoster::Private::_RemoveTranslators(const node_ref* nodeRef,
|
1086 | 1107 | iterator = next; |
1087 | 1108 | } |
1088 | 1109 | |
1089 | | // Unload image from the removed translator |
1090 | | |
1091 | | if (image >= B_OK) |
1092 | | unload_add_on(image); |
1093 | | |
1094 | 1110 | _NotifyListeners(update); |
1095 | 1111 | } |
1096 | 1112 | |
… |
… |
BTranslatorRoster::Private::_NotifyListeners(BMessage& update) const
|
1150 | 1166 | |
1151 | 1167 | // #pragma mark - |
1152 | 1168 | |
| 1169 | BTranslatorReleaseDelegate::BTranslatorReleaseDelegate(BTranslator* it) |
| 1170 | : |
| 1171 | fUnderlying(it) |
| 1172 | { |
| 1173 | } |
| 1174 | |
| 1175 | void BTranslatorReleaseDelegate::Release() |
| 1176 | { |
| 1177 | fUnderlying->Release(); |
| 1178 | // ReleaseDelegate is only allowed to release a translator once. |
| 1179 | delete this; |
| 1180 | } |
| 1181 | |
1153 | 1182 | |
1154 | 1183 | BTranslatorRoster::BTranslatorRoster() |
1155 | 1184 | { |
… |
… |
BTranslatorRoster::MakeConfigurationView(translator_id id,
|
1664 | 1693 | return translator->MakeConfigurationView(ioExtension, _view, _extent); |
1665 | 1694 | } |
1666 | 1695 | |
| 1696 | BTranslatorReleaseDelegate* |
| 1697 | BTranslatorRoster::AcquireTranslator(int32 id) |
| 1698 | { |
| 1699 | BAutolock locker(fPrivate); |
| 1700 | |
| 1701 | BTranslator* translator = fPrivate->FindTranslator(id); |
| 1702 | if (translator == NULL) |
| 1703 | return NULL; |
| 1704 | |
| 1705 | translator->Acquire(); |
| 1706 | return new BTranslatorReleaseDelegate(translator); |
| 1707 | } |
1667 | 1708 | |
1668 | 1709 | /*! |
1669 | 1710 | Gets the configuration setttings for the translator |
diff --git a/src/kits/translation/TranslatorRosterPrivate.h b/src/kits/translation/TranslatorRosterPrivate.h
index dac2c01..956f980 100644
a
|
b
|
typedef std::map<translator_id, translator_item> TranslatorMap;
|
33 | 33 | typedef std::vector<BMessenger> MessengerList; |
34 | 34 | typedef std::vector<node_ref> NodeRefList; |
35 | 35 | typedef std::set<entry_ref> EntryRefSet; |
| 36 | typedef std::map<image_id, int32> ImageMap; |
| 37 | typedef std::map<BTranslator*, image_id> TranslatorImageMap; |
36 | 38 | |
37 | 39 | |
38 | 40 | class BTranslatorRoster::Private : public BHandler, public BLocker { |
… |
… |
public:
|
78 | 80 | status_t StartWatching(BMessenger target); |
79 | 81 | status_t StopWatching(BMessenger target); |
80 | 82 | |
81 | | void TranslatorDeleted(translator_id id); |
82 | | |
83 | 83 | private: |
84 | 84 | static int _CompareSupport(const void* _a, const void* _b); |
85 | 85 | |
… |
… |
private:
|
107 | 107 | const char* name); |
108 | 108 | void _EntryAdded(const entry_ref& ref); |
109 | 109 | void _NotifyListeners(BMessage& update) const; |
| 110 | void _TranslatorDeleted(translator_id id, |
| 111 | BTranslator *self); |
| 112 | |
110 | 113 | |
111 | 114 | NodeRefList fDirectories; |
112 | 115 | TranslatorMap fTranslators; |
113 | 116 | MessengerList fMessengers; |
114 | 117 | EntryRefSet fRescanEntries; |
| 118 | ImageMap fKnownImages; |
| 119 | TranslatorImageMap fImageOrigins; |
115 | 120 | const char* fABISubDirectory; |
116 | 121 | int32 fNextID; |
117 | 122 | bool fLazyScanning; |
diff --git a/src/preferences/datatranslations/DataTranslationsWindow.cpp b/src/preferences/datatranslations/DataTranslationsWindow.cpp
index 2d71c40..4543c81 100644
a
|
b
|
DataTranslationsWindow::DataTranslationsWindow()
|
53 | 53 | : |
54 | 54 | BWindow(BRect(0, 0, 550, 350), B_TRANSLATE_SYSTEM_NAME("DataTranslations"), |
55 | 55 | B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE |
56 | | | B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS) |
| 56 | | B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS), |
| 57 | fRelease(NULL) |
57 | 58 | { |
58 | 59 | MoveTo(DataTranslationsSettings::Instance()->WindowCorner()); |
59 | 60 | |
… |
… |
DataTranslationsWindow::_ShowConfigView(int32 id)
|
151 | 152 | fRightBox->RemoveChild(fConfigView); |
152 | 153 | delete fConfigView; |
153 | 154 | fConfigView = NULL; |
| 155 | if (fRelease != NULL) { |
| 156 | fRelease->Release(); |
| 157 | fRelease = NULL; |
| 158 | } |
154 | 159 | } |
155 | 160 | |
156 | 161 | BMessage emptyMsg; |
… |
… |
DataTranslationsWindow::_ShowConfigView(int32 id)
|
165 | 170 | // force config views to all have the same color |
166 | 171 | fRightBox->AddChild(fConfigView); |
167 | 172 | |
| 173 | // Make sure the translator's image doesn't get unloaded while we are still |
| 174 | // showing a config view whose code is in the image |
| 175 | fRelease = roster->AcquireTranslator(id); |
| 176 | |
168 | 177 | return B_OK; |
169 | 178 | } |
170 | 179 | |
… |
… |
DataTranslationsWindow::_ShowInfoView()
|
176 | 185 | fRightBox->RemoveChild(fConfigView); |
177 | 186 | delete fConfigView; |
178 | 187 | fConfigView = NULL; |
| 188 | if (fRelease != NULL) { |
| 189 | fRelease->Release(); |
| 190 | fRelease = NULL; |
| 191 | } |
| 192 | |
179 | 193 | } |
180 | 194 | |
181 | 195 | BTextView* view = new BTextView("info text"); |
diff --git a/src/preferences/datatranslations/DataTranslationsWindow.h b/src/preferences/datatranslations/DataTranslationsWindow.h
index 68e4207..ce988ec 100644
a
|
b
|
|
20 | 20 | |
21 | 21 | #include "TranslatorListView.h" |
22 | 22 | |
| 23 | class BTranslatorReleaseDelegate; |
23 | 24 | |
24 | 25 | class DataTranslationsWindow : public BWindow { |
25 | 26 | public: |
… |
… |
private:
|
40 | 41 | |
41 | 42 | TranslatorListView* fTranslatorListView; |
42 | 43 | |
| 44 | BTranslatorReleaseDelegate* fRelease; |
| 45 | |
43 | 46 | BBox* fRightBox; |
44 | 47 | BView* fConfigView; |
45 | 48 | IconView* fIconView; |