* Made sure that more threads have the unstructured exception handler installed (avoids crashing the entire process in case of access violations), and thread naming for more threads.
* Ensure that if an exception occurs while rendering the diagnostic window, the window is closed instead of it stopping to respond to window events.
void run() // noexcept
{
- win32_exception::install_handler();
- detail::SetThreadName(GetCurrentThreadId(), name_.c_str());
+ win32_exception::ensure_handler_installed_for_thread(name_.c_str());
+
while(is_running_)
{
try
return;\r
}\r
} \r
- glClear(GL_COLOR_BUFFER_BIT);\r
- window_->Draw(*this);\r
- window_->Display();\r
- boost::this_thread::sleep(boost::posix_time::milliseconds(10));\r
+\r
+ try\r
+ {\r
+ glClear(GL_COLOR_BUFFER_BIT);\r
+ window_->Draw(*this);\r
+ window_->Display();\r
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));\r
+ }\r
+ catch (...)\r
+ {\r
+ CASPAR_LOG_CURRENT_EXCEPTION();\r
+ CASPAR_LOG(error)\r
+ << L"Closing diag window due to error during rendering";\r
+ window_.reset();\r
+ return;\r
+ }\r
+\r
executor_.begin_invoke([this]{tick();});\r
}\r
\r
\r
#include "../stdafx.h"\r
\r
+#include <boost/thread.hpp>\r
+\r
#include "win32_exception.h"\r
\r
#include "../log/log.h"\r
+#include "../concurrency/executor.h"\r
\r
namespace caspar {\r
\r
+bool& installed_for_thread()\r
+{\r
+ static boost::thread_specific_ptr<bool> installed;\r
+\r
+ auto for_thread = installed.get();\r
+ \r
+ if (!for_thread)\r
+ {\r
+ for_thread = new bool(false);\r
+ installed.reset(for_thread);\r
+ }\r
+\r
+ return *for_thread;\r
+}\r
+\r
void win32_exception::install_handler() \r
{\r
//#ifndef _DEBUG\r
_set_se_translator(win32_exception::Handler);\r
+ installed_for_thread() = true;\r
//#endif\r
}\r
\r
+void win32_exception::ensure_handler_installed_for_thread(\r
+ const char* thread_description)\r
+{\r
+ if (!installed_for_thread())\r
+ {\r
+ install_handler();\r
+\r
+ if (thread_description)\r
+ detail::SetThreadName(GetCurrentThreadId(), thread_description);\r
+ }\r
+}\r
+\r
void win32_exception::Handler(unsigned int errorCode, EXCEPTION_POINTERS* pInfo) {\r
switch(errorCode)\r
{\r
public:\r
typedef const void* address;\r
static void install_handler();\r
+ static void ensure_handler_installed_for_thread(\r
+ const char* thread_description = nullptr);\r
\r
address location() const { return location_; }\r
unsigned int error_code() const { return errorCode_; }\r
pointer address(reference x) const { return &x; }\r
const_pointer address(const_reference x) const { return &x; }\r
page_locked_allocator<T>& operator=(const page_locked_allocator&) { return *this; }\r
+ bool operator!=(const page_locked_allocator&) const { return false; }\r
+ bool operator==(const page_locked_allocator&) const { return true; }\r
void construct(pointer p, const T& val) { new ((T*) p) T(val); }\r
void destroy(pointer p) { p->~T(); }\r
\r
#include "decklink_consumer.h"
#include "../util/util.h"
+#include "../util/decklink_allocator.h"
#include "../interop/DeckLinkAPI_h.h"
const int channel_index_;
const configuration config_;
+ std::unique_ptr<thread_safe_decklink_allocator> allocator_;
CComPtr<IDeckLink> decklink_;
CComQIPtr<IDeckLinkOutput> output_;
CComQIPtr<IDeckLinkKeyer> keyer_;
void enable_video(BMDDisplayMode display_mode)
{
+ if (config_.custom_allocator)
+ {
+ allocator_.reset(new thread_safe_decklink_allocator(print()));
+
+ if (FAILED(output_->SetVideoOutputFrameMemoryAllocator(allocator_.get())))
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set custom memory allocator."));
+
+ CASPAR_LOG(info) << print() << L" Using custom allocator.";
+ }
+
if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output."));
}
#include "decklink_consumer.h"\r
\r
#include "../util/util.h"\r
+#include "../util/decklink_allocator.h"\r
\r
#include "../interop/DeckLinkAPI_h.h"\r
\r
#include <common/concurrency/future_util.h>\r
#include <common/diagnostics/graph.h>\r
#include <common/exception/exceptions.h>\r
+#include <common/exception/win32_exception.h>\r
#include <common/utility/assert.h>\r
\r
#include <core/parameters/parameters.h>\r
namespace caspar { namespace decklink { \r
\r
struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable\r
-{ \r
+{\r
const int channel_index_;\r
const configuration config_;\r
\r
+ std::unique_ptr<thread_safe_decklink_allocator> allocator_;\r
CComPtr<IDeckLink> decklink_;\r
CComQIPtr<IDeckLinkOutput> output_;\r
CComQIPtr<IDeckLinkKeyer> keyer_;\r
\r
void enable_video(BMDDisplayMode display_mode)\r
{\r
+ if (config_.custom_allocator)\r
+ {\r
+ allocator_.reset(new thread_safe_decklink_allocator(print()));\r
+\r
+ if (FAILED(output_->SetVideoOutputFrameMemoryAllocator(allocator_.get())))\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set custom memory allocator."));\r
+\r
+ CASPAR_LOG(info) << print() << L" Using custom allocator.";\r
+ }\r
+\r
if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault))) \r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output."));\r
\r
\r
STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result))\r
{\r
+ win32_exception::ensure_handler_installed_for_thread("decklink-ScheduledFrameCompleted");\r
if(!is_running_)\r
return E_FAIL;\r
\r
\r
STDMETHOD(RenderAudioSamples(BOOL preroll))\r
{\r
+ win32_exception::ensure_handler_installed_for_thread("decklink-RenderAudioSamples");\r
+\r
if(!is_running_)\r
return E_FAIL;\r
\r
else if(latency == L"normal")\r
config.latency = configuration::normal_latency;\r
\r
- config.key_only = ptree.get(L"key-only", config.key_only);\r
- config.device_index = ptree.get(L"device", config.device_index);\r
- config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);\r
- config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);\r
+ config.key_only = ptree.get(L"key-only", config.key_only);\r
+ config.device_index = ptree.get(L"device", config.device_index);\r
+ config.embedded_audio = ptree.get(L"embedded-audio", config.embedded_audio);\r
+ config.base_buffer_depth = ptree.get(L"buffer-depth", config.base_buffer_depth);\r
+ config.custom_allocator = ptree.get(L"custom-allocator", config.custom_allocator);\r
config.audio_layout =\r
core::default_channel_layout_repository().get_by_name(\r
boost::to_upper_copy(ptree.get(L"channel-layout", L"STEREO")));\r
<ClInclude Include="interop\DeckLinkAPI_h.h" />\r
<ClInclude Include="producer\decklink_producer.h" />\r
<ClInclude Include="StdAfx.h" />\r
+ <ClInclude Include="util\decklink_allocator.h" />\r
<ClInclude Include="util\util.h" />\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="consumer\blocking_decklink_consumer.h">\r
<Filter>source\consumer</Filter>\r
</ClInclude>\r
+ <ClInclude Include="util\decklink_allocator.h">\r
+ <Filter>source\util</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
\r
#include "../interop/DeckLinkAPI_h.h"\r
#include "../util/util.h"\r
+#include "../util/decklink_allocator.h"\r
\r
#include "../../ffmpeg/producer/filter/filter.h"\r
#include "../../ffmpeg/producer/util/util.h"\r
#include <common/concurrency/com_context.h>\r
#include <common/diagnostics/graph.h>\r
#include <common/exception/exceptions.h>\r
+#include <common/exception/win32_exception.h>\r
#include <common/log/log.h>\r
#include <common/memory/memclr.h>\r
\r
boost::timer tick_timer_;\r
boost::timer frame_timer_;\r
\r
+ std::unique_ptr<thread_safe_decklink_allocator> allocator_;\r
CComPtr<IDeckLink> decklink_;\r
CComQIPtr<IDeckLinkInput> input_;\r
CComQIPtr<IDeckLinkAttributes > attributes_;\r
diagnostics::register_graph(graph_);\r
\r
auto display_mode = get_display_mode(input_, format_desc_.format, bmdFormat8BitYUV, bmdVideoInputFlagDefault);\r
- \r
+ \r
+ allocator_.reset(new thread_safe_decklink_allocator(print()));\r
+\r
+ if(FAILED(input_->SetVideoInputFrameMemoryAllocator(allocator_.get()))) \r
+ BOOST_THROW_EXCEPTION(caspar_exception()\r
+ << msg_info(narrow(print()) + " Could not enable use of custom allocator.")\r
+ << boost::errinfo_api_function("SetVideoInputFrameMemoryAllocator"));\r
+\r
// NOTE: bmdFormat8BitARGB is currently not supported by any decklink card. (2011-05-08)\r
if(FAILED(input_->EnableVideoInput(display_mode, bmdFormat8BitYUV, bmdVideoInputFlagDefault))) \r
BOOST_THROW_EXCEPTION(caspar_exception() \r
\r
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame* video, IDeckLinkAudioInputPacket* audio)\r
{ \r
+ win32_exception::ensure_handler_installed_for_thread("decklink-VideoInputFrameArrived");\r
if(!video)\r
return S_OK;\r
\r
--- /dev/null
+/*
+* Copyright 2013 Sveriges Television AB http://casparcg.com/
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+
+#include <boost/thread.hpp>
+
+#include <tbb/concurrent_queue.h>
+#include <tbb/concurrent_unordered_map.h>
+#include <tbb/atomic.h>
+
+#include <common/utility/assert.h>
+//#include <common/memory/page_locked_allocator.h>
+#include <common/exception/win32_exception.h>
+
+#include "../interop/DeckLinkAPI_h.h"
+
+namespace caspar { namespace decklink {
+
+class thread_safe_decklink_allocator : public IDeckLinkMemoryAllocator
+{
+ typedef std::shared_ptr<
+ std::vector<uint8_t, tbb::cache_aligned_allocator<uint8_t>>> buffer;
+
+ std::wstring print_;
+ tbb::concurrent_unordered_map<size_t, tbb::concurrent_queue<void*>> free_;
+ tbb::concurrent_unordered_map<void*, buffer> buffers_;
+ tbb::atomic<size_t> total_allocated_;
+ tbb::atomic<int64_t> total_calls_;
+public:
+ thread_safe_decklink_allocator(const std::wstring& print)
+ : print_(print)
+ {
+ total_allocated_ = 0;
+ total_calls_ = 0;
+ }
+
+ ~thread_safe_decklink_allocator()
+ {
+ CASPAR_LOG(debug)
+ << print_
+ << L" allocated a total of " << total_allocated_ << L" bytes"
+ << L" and was called " << total_calls_ << L" times"
+ << L" during playout";
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE AllocateBuffer(
+ unsigned long buffer_size, void** allocated_buffer)
+ {
+ win32_exception::ensure_handler_installed_for_thread(
+ "decklink-allocator");
+
+ try
+ {
+ *allocated_buffer = allocate_buffer(buffer_size);
+ }
+ catch (const std::bad_alloc&)
+ {
+ CASPAR_LOG_CURRENT_EXCEPTION();
+
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ void* allocate_buffer(unsigned long buffer_size)
+ {
+ ++total_calls_;
+ void* result;
+
+ if (!free_[buffer_size].try_pop(result))
+ {
+ auto buf = std::make_shared<std::vector<uint8_t,
+ tbb::cache_aligned_allocator<uint8_t>>>(buffer_size);
+ result = buf->data();
+ buffers_.insert(std::make_pair(result, buf));
+
+ total_allocated_ += buffer_size;
+
+ CASPAR_LOG(debug)
+ << print_
+ << L" allocated buffer of size: " << buffer_size
+ << L" for a total of " << total_allocated_;
+ }
+
+ return result;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(void* b)
+ {
+ win32_exception::ensure_handler_installed_for_thread(
+ "decklink-allocator");
+
+ try
+ {
+ release_buffer(b);
+ }
+ catch (const std::bad_alloc&)
+ {
+ CASPAR_LOG_CURRENT_EXCEPTION();
+ }
+
+ return S_OK;
+ }
+
+ void release_buffer(void* b)
+ {
+ auto buf = buffers_[b];
+
+ CASPAR_VERIFY(buf);
+
+ free_[buf->size()].push(b);
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Commit()
+ {
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Decommit()
+ {
+ return S_OK;
+ }
+
+ STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}
+ STDMETHOD_(ULONG, AddRef()) {return 1;}
+ STDMETHOD_(ULONG, Release()) {return 1;}
+};
+
+}}
latency_t latency;\r
bool key_only;\r
size_t base_buffer_depth;\r
+ bool custom_allocator;\r
\r
configuration()\r
: device_index(1)\r
, latency(default_latency)\r
, key_only(false)\r
, base_buffer_depth(3)\r
+ , custom_allocator(true)\r
{\r
}\r
\r
#include "producer/ffmpeg_producer.h"\r
\r
#include <common/log/log.h>\r
+#include <common/exception/win32_exception.h>\r
\r
#include <core/parameters/parameters.h>\r
#include <core/consumer/frame_consumer.h>\r
\r
int ffmpeg_lock_callback(void **mutex, enum AVLockOp op) \r
{ \r
+ win32_exception::ensure_handler_installed_for_thread("ffmpeg-thread");\r
if(!mutex)\r
return 0;\r
\r
\r
void log_for_thread(void* ptr, int level, const char* fmt, va_list vl)\r
{\r
+ win32_exception::ensure_handler_installed_for_thread("ffmpeg-thread");\r
//if (get_disable_logging_for_thread().get() == nullptr) // It does not matter what the value of the bool is\r
log_callback(ptr, level, fmt, vl);\r
}\r
///////////////////\r
HRESULT STDMETHODCALLTYPE FlashAxContainer::QueryService( REFGUID rsid, REFIID riid, void** ppvObj) \r
{\r
+ win32_exception::ensure_handler_installed_for_thread("flash-player-thread");\r
// ATLTRACE(_T("IServiceProvider::QueryService\n"));\r
//the flashcontrol asks for an interface {618F8AD4-8B7A-11D0-8FCC-00C04FD9189D}, this is IID for a DirectDraw3 object\r
\r
\r
void STDMETHODCALLTYPE FlashAxContainer::OnFlashCall(BSTR request)\r
{\r
+ win32_exception::ensure_handler_installed_for_thread("flash-player-thread");\r
std::wstring str(request);\r
if(str.find(TEXT("DisplayedTemplate")) != std::wstring::npos)\r
{\r
\r
#include "image_consumer.h"\r
\r
+#include <common/exception/win32_exception.h>\r
#include <common/exception/exceptions.h>\r
#include <common/env.h>\r
#include <common/log/log.h>\r
\r
boost::thread async([format_desc, frame, filename]\r
{\r
+ win32_exception::ensure_handler_installed_for_thread("image-consumer-thread");\r
+\r
try\r
{\r
auto filename2 = filename;\r
#include <common/utility/timer.h>
#include <common/utility/string.h>
#include <common/concurrency/future_util.h>
+#include <common/exception/win32_exception.h>
#include <core/parameters/parameters.h>
#include <core/consumer/frame_consumer.h>
// oal_consumer
virtual bool OnGetData(sf::SoundStream::Chunk& data) override
- {
+ {
+ win32_exception::ensure_handler_installed_for_thread(
+ "sfml-audio-thread");
std::shared_ptr<std::vector<audio_buffer_16>> audio_data;
if (!input_.try_pop(audio_data))
#include <common/utility/timer.h>\r
#include <common/utility/string.h>\r
#include <common/concurrency/future_util.h>\r
+#include <common/concurrency/executor.h>\r
+#include <common/exception/win32_exception.h>\r
\r
#include <ffmpeg/producer/filter/filter.h>\r
\r
\r
void run()\r
{\r
+ win32_exception::ensure_handler_installed_for_thread(\r
+ "ogl-consumer-thread");\r
+\r
try\r
{\r
init();\r
#include <common/log/log.h>
#include <common/exception/exceptions.h>
+#include <common/exception/win32_exception.h>
#include <common/concurrency/future_util.h>
#include <common/diagnostics/graph.h>
PaStreamCallbackFlags status_flags,
void* user_data)
{
+ win32_exception::ensure_handler_installed_for_thread(
+ "portaudio-callback-thread");
auto consumer = static_cast<portaudio_consumer*>(user_data);
consumer->write_samples(
#include <boost/asio/io_service.hpp>
#include <boost/thread/thread.hpp>
+#include <common/exception/win32_exception.h>
+
namespace caspar { namespace protocol { namespace asio {
struct io_service_manager::impl
impl()
: work_(new boost::asio::io_service::work(service_))
- , thread_(std::bind(&boost::asio::io_service::run, &service_))
+ , thread_([this] { run(); })
{
}
+ void run()
+ {
+ win32_exception::ensure_handler_installed_for_thread("asio-thread");
+
+ service_.run();
+ }
+
~impl()
{
work_.reset();
#include "oscpack/oscOutboundPacketStream.h"
#include <common/utility/string.h>
+#include <common/exception/win32_exception.h>
#include <functional>
#include <vector>
void on_next(const core::monitor::message& msg)
{
+ win32_exception::ensure_handler_installed_for_thread("agents-thread");
auto data_ptr = make_safe<std::vector<char>>(write_osc_event(msg));
tbb::spin_mutex::scoped_lock lock(endpoints_mutex_);
CloseHandle(handle_);\r
}\r
\r
-bool Thread::static_bInstallWin32ExceptionHandler_ = true;\r
-\r
Thread::Thread() : pRunnable_(0), hThread_(0), stopEvent_(TRUE, FALSE), timeout_(10000) {\r
}\r
\r
return returnValue;\r
}\r
\r
-void Thread::EnableWin32ExceptionHandler(bool bEnable) {\r
- static_bInstallWin32ExceptionHandler_ = bEnable;\r
-}\r
-\r
DWORD WINAPI Thread::ThreadEntrypoint(LPVOID pParam) {\r
Thread* pThis = reinterpret_cast<Thread*>(pParam);\r
\r
- if(Thread::static_bInstallWin32ExceptionHandler_)\r
- win32_exception::install_handler();\r
+ win32_exception::install_handler();\r
\r
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);\r
\r
\r
bool IsRunning();\r
\r
- static void EnableWin32ExceptionHandler(bool bEnable);\r
-\r
void SetTimeout(DWORD timeout) {\r
timeout_ = timeout;\r
}\r
Event stopEvent_;\r
\r
DWORD timeout_;\r
- static bool static_bInstallWin32ExceptionHandler_;\r
};\r
\r
}
\ No newline at end of file
<keyer>external [external|internal|default]</keyer>\r
<key-only>false [true|false]</key-only>\r
<buffer-depth>3 [1..]</buffer-depth>\r
+ <custom-allocator>true [true|false]</custom-allocator>\r
</decklink>\r
<blocking-decklink>\r
<device>[1..]</device>\r
<channel-layout>stereo [mono|stereo|dts|dolbye|dolbydigital|smpte|passthru]</channel-layout>\r
<keyer>external [external|internal|default]</keyer>\r
<key-only>false [true|false]</key-only>\r
+ <custom-allocator>true [true|false]</custom-allocator>\r
</blocking-decklink>\r
<bluefish>\r
<device>[1..]</device>\r
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);\r
\r
// Install structured exception handler.\r
- caspar::win32_exception::install_handler();\r
+ caspar::win32_exception::ensure_handler_installed_for_thread("main-thread");\r
\r
// Increase time precision. This will increase accuracy of function like Sleep(1) from 10 ms to 1 ms.\r
struct inc_prec\r
tbb_thread_installer(){observe(true);}\r
void on_scheduler_entry(bool is_worker)\r
{\r
- //caspar::detail::SetThreadName(GetCurrentThreadId(), "tbb-worker-thread");\r
- caspar::win32_exception::install_handler();\r
+ caspar::win32_exception::ensure_handler_installed_for_thread("tbb-worker-thread");\r
}\r
} tbb_thread_installer;\r
\r
// anyway when the main thread terminates.\r
boost::thread stdin_thread([&caspar_server, &shutdown_server_now, &wait_for_keypress]\r
{\r
+ caspar::win32_exception::ensure_handler_installed_for_thread("stdin-thread");\r
+\r
// Create a amcp parser for console commands.\r
caspar::protocol::amcp::AMCPProtocolStrategy amcp(\r
caspar_server.get_channels(),\r