/* * Copyright (c) 2002, 2003 Marcus Overhagen * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files or portions * thereof (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject * to the following conditions: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice * in the binary, as well as this list of conditions and the following * disclaimer in the documentation and/or other materials provided with * the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ /*! This is a management class for dormant media nodes. It is private to the media kit and only accessed by the BMediaRoster class and the media_addon_server. It handles loading/unloading of dormant nodes. Dormant media nodes can be instantiated on demand. The reside on harddisk in the directories /boot/beos/system/add-ons/media and /boot/home/config/add-ons/media. Multiple media nodes can be included in one file, they can be accessed using the BMediaAddOn that each file implements. The BMediaAddOn allows getting a list of supported flavors. Each flavor represents a media node. The media_addon_server does the initial scanning of files and getting the list of supported flavors. It uses the flavor_info to do this, and reports the list of flavors to the media_server packed into individual dormant_media_node structures. */ #include "DormantNodeManager.h" #include #include #include #include #include #include #include #include #include namespace BPrivate { namespace media { DormantNodeManager* gDormantNodeManager; // initialized by BMediaRoster. DormantNodeManager::DormantNodeManager() : fLock("dormant node manager locker") { } DormantNodeManager::~DormantNodeManager() { // force unloading all currently loaded images AddOnMap::iterator iterator = fAddOnMap.begin(); for (; iterator != fAddOnMap.end(); iterator++) { loaded_add_on_info& info = iterator->second; ERROR("Forcing unload of add-on id %" B_PRId32 " with usecount %" B_PRId32 "\n", info.add_on->AddonID(), info.use_count); _UnloadAddOn(info.add_on, info.image); } } BMediaAddOn* DormantNodeManager::GetAddOn(media_addon_id id) { TRACE("DormantNodeManager::GetAddon, id %" B_PRId32 "\n", id); // first try to use a already loaded add-on BMediaAddOn* addOn = _LookupAddOn(id); if (addOn != NULL) return addOn; // Be careful, we avoid locking here! // ok, it's not loaded, try to get the path BPath path; if (FindAddOnPath(&path, id) != B_OK) { ERROR("DormantNodeManager::GetAddon: can't find path for add-on %" B_PRId32 "\n", id); return NULL; } // try to load it BMediaAddOn* newAddOn; image_id image; if (_LoadAddOn(path.Path(), id, &newAddOn, &image) != B_OK) { ERROR("DormantNodeManager::GetAddon: can't load add-on %" B_PRId32 " from path %s\n",id, path.Path()); return NULL; } // ok, we successfully loaded it. Now lock and insert it into the map, // or unload it if the map already contains one that was loaded by another // thread at the same time BAutolock _(fLock); addOn = _LookupAddOn(id); if (addOn == NULL) { // we use the loaded one addOn = newAddOn; // and save it into the list loaded_add_on_info info; info.add_on = newAddOn; info.image = image; info.use_count = 1; try { fAddOnMap.insert(std::make_pair(id, info)); } catch (std::bad_alloc& exception) { _UnloadAddOn(newAddOn, image); return NULL; } } else _UnloadAddOn(newAddOn, image); ASSERT(addOn->AddonID() == id); return addOn; } void DormantNodeManager::PutAddOn(media_addon_id id) { TRACE("DormantNodeManager::PutAddon, id %" B_PRId32 "\n", id); BAutolock locker(fLock); AddOnMap::iterator found = fAddOnMap.find(id); if (found == fAddOnMap.end()) { ERROR("DormantNodeManager::PutAddon: failed to find add-on %" B_PRId32 "\n", id); return; } loaded_add_on_info& info = found->second; if (--info.use_count == 0) { // unload add-on BMediaAddOn* addOn = info.add_on; image_id image = info.image; fAddOnMap.erase(found); locker.Unlock(); _UnloadAddOn(addOn, image); } } void DormantNodeManager::PutAddOnDelayed(media_addon_id id) { // Called from a node destructor of the loaded media-add-on. // We must make sure that the media-add-on stays in memory // a couple of seconds longer. UNIMPLEMENTED(); } //! For use by media_addon_server only media_addon_id DormantNodeManager::RegisterAddOn(const char* path) { TRACE("DormantNodeManager::RegisterAddon, path %s\n", path); entry_ref ref; status_t status = get_ref_for_path(path, &ref); if (status != B_OK) { ERROR("DormantNodeManager::RegisterAddon failed, couldn't get ref " "for path %s: %s\n", path, strerror(status)); return 0; } server_register_add_on_request request; request.ref = ref; server_register_add_on_reply reply; status = QueryServer(SERVER_REGISTER_ADD_ON, &request, sizeof(request), &reply, sizeof(reply)); if (status != B_OK) { ERROR("DormantNodeManager::RegisterAddon failed, couldn't talk to " "media server: %s\n", strerror(status)); return 0; } TRACE("DormantNodeManager::RegisterAddon finished with id %" B_PRId32 "\n", reply.add_on_id); return reply.add_on_id; } //! For use by media_addon_server only void DormantNodeManager::UnregisterAddOn(media_addon_id id) { TRACE("DormantNodeManager::UnregisterAddon id %" B_PRId32 "\n", id); ASSERT(id > 0); port_id port = find_port(MEDIA_SERVER_PORT_NAME); if (port < 0) return; server_unregister_add_on_command msg; msg.add_on_id = id; write_port(port, SERVER_UNREGISTER_ADD_ON, &msg, sizeof(msg)); } status_t DormantNodeManager::FindAddOnPath(BPath* path, media_addon_id id) { server_get_add_on_ref_request request; request.add_on_id = id; server_get_add_on_ref_reply reply; status_t status = QueryServer(SERVER_GET_ADD_ON_REF, &request, sizeof(request), &reply, sizeof(reply)); if (status != B_OK) return status; entry_ref ref = reply.ref; return path->SetTo(&ref); } BMediaAddOn* DormantNodeManager::_LookupAddOn(media_addon_id id) { BAutolock _(fLock); AddOnMap::iterator found = fAddOnMap.find(id); if (found == fAddOnMap.end()) return NULL; loaded_add_on_info& info = found->second; ASSERT(id == info.add_on->AddonID()); info.use_count++; return info.add_on; } status_t DormantNodeManager::_LoadAddOn(const char* path, media_addon_id id, BMediaAddOn** _newAddOn, image_id* _newImage) { image_id image = load_add_on(path); if (image < 0) { ERROR("DormantNodeManager::LoadAddon: loading \"%s\" failed: %s\n", path, strerror(image)); return image; } BMediaAddOn* (*makeAddOn)(image_id); status_t status = get_image_symbol(image, "make_media_addon", B_SYMBOL_TYPE_TEXT, (void**)&makeAddOn); if (status != B_OK) { ERROR("DormantNodeManager::LoadAddon: loading failed, function not " "found: %s\n", strerror(status)); unload_add_on(image); return status; } BMediaAddOn* addOn = makeAddOn(image); if (addOn == NULL) { ERROR("DormantNodeManager::LoadAddon: creating BMediaAddOn failed\n"); unload_add_on(image); return B_ERROR; } ASSERT(addOn->ImageID() == image); // this should be true for a well behaving add-ons // We are a friend class of BMediaAddOn and initialize these member // variables addOn->fAddon = id; addOn->fImage = image; // everything ok *_newAddOn = addOn; *_newImage = image; return B_OK; } void DormantNodeManager::_UnloadAddOn(BMediaAddOn* addOn, image_id image) { ASSERT(addOn != NULL); ASSERT(addOn->ImageID() == image); // if this fails, something bad happened to the add-on delete addOn; unload_add_on(image); } } // namespace media } // namespace BPrivate