UWP Custom Video Effect - c#

I'm developing an application for detecting motion within webcam frames.
For this, I'm using IBasicVideoEffect for extracting frames one by one from MediaCapture. I have created class CustomEffect which inherits IBasicVideoEffect. I have used OpenCV for motion detection, it is working fine. It is also giving me motion detection level. I want to raise event from CustomEffect if motion level is greater than threshold.
But for videoDefination code is:
var videoDefinition = new VideoEffectDefinition(typeof(CustomEffect).ToString());
Here for videoDefinition constructor it is asking for ClassID,
How can i get event from CustomEffect object.
I want to raise custom event from CustomEffect (eg.: MotionDetectedEvent )
Here is my CustomEffect class:
public sealed class CustomEffect : IBasicVideoEffect
{
private OpenCVHelper _helper;
private IPropertySet _configuration;
internal event EventHandler<EventArgs> MotionDetected;
public void SetProperties(IPropertySet configuration)
{
_configuration = configuration;
}
public void SetEncodingProperties(VideoEncodingProperties encodingProperties,
IDirect3DDevice device)
{
}
private bool IsToDetectMotion
{
get
{
object val;
if (_configuration != null &&
_configuration.TryGetValue("IsToDetectMotion", out val))
return (bool) val;
return false;
}
}
public void ProcessFrame(ProcessVideoFrameContext context)
{
var tempBitmap = context.OutputFrame.SoftwareBitmap;
context.InputFrame.SoftwareBitmap.CopyTo(tempBitmap);
var originalBitmap = SoftwareBitmap.Convert(tempBitmap, BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight);
var outputBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8,
originalBitmap.PixelWidth, originalBitmap.PixelHeight,
BitmapAlphaMode.Straight);
if (!IsToDetectMotion)
{
context.InputFrame.SoftwareBitmap.CopyTo(context.OutputFrame.SoftwareBitmap);
return;
}
if (_helper == null)
_helper = new OpenCVHelper();
var level = _helper.MotionDetector(tempBitmap, outputBitmap);
RaiseMotionDetectedEvent();
Debug.WriteLine(level.ToString());
outputBitmap.CopyTo(context.OutputFrame.SoftwareBitmap);
}
private void RaiseMotionDetectedEvent()
{
if (MotionDetected != null)
MotionDetected(this, new EventArgs());
}
public void Close(MediaEffectClosedReason reason)
{
}
public void DiscardQueuedFrames()
{
}
public bool IsReadOnly { get; }
public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties
{
get
{
var encodingProperties = new VideoEncodingProperties();
encodingProperties.Subtype = "ARGB32";
return new List<VideoEncodingProperties> {encodingProperties};
// If the list is empty, the encoding type will be ARGB32.
// return new List<VideoEncodingProperties>();
}
}
public MediaMemoryTypes SupportedMemoryTypes { get; }
public bool TimeIndependent { get; }
}

//in Windows Runtime Component
public sealed class FrameArgs
{
public FrameArgs(int frameCount)
{
FrameCount = frameCount;
}
public int FrameCount
{ get; }
}
public sealed partial class CustomEffect
{
#region ProcessFrameCompleted
public EventHandler<Object> ProcessFrameCompleted
{
get
{
object val;
if (configuration != null && configuration.TryGetValue(nameof(ProcessFrameCompleted), out val))
{
return (EventHandler<Object>)val;
}
return null;
}
}
public void RaiseProcessFrameCompleted(FrameArgs args)
{
ProcessFrameCompleted?.Invoke(null, (Object)args);
}
#endregion
//call as necessary
//RaiseProcessFrameCompleted(new FrameArgs(frameCount));
}
//in your app
public static async Task<IMediaExtension> AddCustomEffect(MediaCapture mediaCapture, EventHandler<FrameArgs> callBack)
{
if (mediaCapture == null)
{
throw new ArgumentException("Parameter cannot be null", nameof(mediaCapture));
}
var videoEffectDefinition =
// ReSharper disable once AssignNullToNotNullAttribute
new VideoEffectDefinition(typeof(CustomEffect).FullName);
var videoEffect =
await mediaCapture.AddVideoEffectAsync(videoEffectDefinition, MediaStreamType.VideoPreview);
videoEffect.SetProperties(
new PropertySet()
{
{
"ProcessFrameCompleted",
new EventHandler<object>((sender, e) =>
{
var args = (FrameArgs)e;
int frameCount = args.FrameCount;
callBack?.Invoke(sender, args);
})
}
});
return videoEffect;
}

Related

Calling SetExecutionStrategy more than once

I am trying to implement an entity framework configuration that deals with deadlocks and retries them. I already have a default execution strategy set in my MyConfiguration constructor. My question is, can I call one after the other, or will they override each other? I am not 100% confident with these so any information would be greatly appreciated.
If I use both in my MyConfiguration constructor, will they override each other or will they actually register both and therefore, both will work?
Here is the code:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
// Trims all strings coming from entity framework
AddInterceptor(new StringTrimmerInterceptor());
SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
: new SqlAzureExecutionStrategy());
SetExecutionStrategy("System.Data.SqlClient", () => new MyCustomExecutionStrategy(5, TimeSpan.FromSeconds(10)));
}
public static bool SuspendExecutionStrategy
{
get
{
return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false;
}
set
{
CallContext.LogicalSetData("SuspendExecutionStrategy", value);
}
}
}
public class StringTrimmerInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new StringTrimmerQueryVisitor());
interceptionContext.Result = new DbQueryCommandTree(
queryCommand.MetadataWorkspace,
queryCommand.DataSpace,
newQuery);
}
}
}
private class StringTrimmerQueryVisitor : DefaultExpressionVisitor
{
private static readonly string[] _typesToTrim = { "nvarchar", "varchar", "char", "nchar" };
public override DbExpression Visit(DbNewInstanceExpression expression)
{
var arguments = expression.Arguments.Select(a =>
{
var propertyArg = a as DbPropertyExpression;
if (propertyArg != null && _typesToTrim.Contains(propertyArg.Property.TypeUsage.EdmType.Name))
{
return EdmFunctions.Trim(a);
}
return a;
});
return DbExpressionBuilder.New(expression.ResultType, arguments);
}
}
}
public static class SqlRetryErrorCodes
{
public const int TimeoutExpired = -2;
public const int Deadlock = 1205;
public const int CouldNotOpenConnection = 53;
public const int TransportFail = 121;
}
public class MyCustomExecutionStrategy : DbExecutionStrategy
{
public MyCustomExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay) { }
private readonly List<int> _errorCodesToRetry = new List<int>
{
SqlRetryErrorCodes.Deadlock,
SqlRetryErrorCodes.TimeoutExpired,
SqlRetryErrorCodes.CouldNotOpenConnection,
SqlRetryErrorCodes.TransportFail
};
protected override bool ShouldRetryOn(Exception exception)
{
var sqlException = exception as SqlException;
if (sqlException != null)
{
foreach (SqlError err in sqlException.Errors)
{
// Enumerate through all errors found in the exception.
if (_errorCodesToRetry.Contains(err.Number))
{
return true;
}
}
}
return false;
}
}
Looking at this post from a member of the .NET team it should override the strategy everytime you call it. The link shows that this can be changed during runtime even (on every ctor-call). In the usage section he states:
Now we can use the flag to disable retry logic for certain operations.
So my (unproved) answer is: You can call it more than one time and it will always have the last set strategy configured.

Xamarin.iOS.UIKit.UIApplication.SendEvent gets called weirdly upon modal close

I'm deriving from UIApplication and overriding SendEvent to capture user taps.
Everything works fine, except when closing modals of type IMvxModalIosView.
Here's the method:
public override void SendEvent(UIEvent uievent)
{
NSSet touches = uievent.AllTouches;
if (touches != null)
{
UITouch touch = (UITouch)touches.AnyObject;
switch (touch.Phase)
{
case UITouchPhase.Ended:
StartDoingStuffAgain();
break;
default:
StopDoingStuff();
break;
}
}
base.SendEvent(uievent);
}
Debugging this while closing an IMvxModalIosView, if I set a breakpoint at the start of the method, as well as StartDoingStuffAgain(), the latter gets hit. If however I only set a breakpoint at StartDoingStuffAgain(), it never gets hit. StopDoingStuff() gets hit regardless.
Why is this?
Edit 1: As per nmilcoff's request for more code:
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
//...
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
//...
_app = application as MyUIApplication;
//...
}
//...
}
[Register("MyUIApplication")]
public class MyUIApplication : UIApplication
{
//...
// Bit of code in original question goes here.
//...
}
//The iPad view presenter (iPhone has MyIPhoneViewPresenter)
public class MyIPadViewPresenter : MvxBaseIosViewPresenter
{
//...
private UINavigationController _navController;
private UINavigationController _modalNavController;
private IMvxMessenger _messenger;
protected readonly SemaphoreSlim _autoLock = new SemaphoreSlim(1, 1);
//...
public override async void Show(MvxViewModelRequest request)
{
await _autoLock.WaitAsync();
//...
if(_messenger == null)
{
_messenger = Mvx.Resolve<IMvxMessenger>();
}
// Among other things, the following method checks if the page
// corresponding to the VM request is a Xamarin.Forms or a native view.
var presentation = MvxIOSPresentationHelpers.CreateView(request, TargetIdiom.Tablet);
switch(presentation.Type)
{
//...
case ViewType.ModalWithNav:
showNavModalView(presentation.ViewController);
break;
//...
}
messenger.Publish(new ViewModelShowMessage(request.ViewModelType, presentation.ViewController, this));
//..
_autoLock.Release();
}
//...
private void showNavModalView(UIViewController viewController)
{
if(_modalNavController == null)
{
_modalNavController = new ModalNavController(viewController);
_modalNavController.ModalPresentationStyle = viewController.ModalPresentationStyle;
_navController.PresentViewController(_modalNavController, false, delegate { });
}
else
{
_modalNavController.PushViewController(viewController, true);
}
//...
}
//...
public override async void ChangePresentation(MvxPresentationHint hint)
{
var close_hint = hint as MvxClosePresentationHint;
if (close_hint != null)
{
await _autoLock.WaitAsync();
close(close_hint.ViewModelToClose);
_autoLock.Release();
}
else
{
//...
}
}
//...
protected void close(IMvxViewModel toClose)
{
//...
_messenger.Publish(new MvxMessage(this));
//...
if (_modalNavController != null)
{
if(_modalNavController.ChildViewControllers.Length <= 1)
{
// This is where the close sequence comes down to.
_modalNavController.DismissViewController(true, delegate{ });
_modalNavController = null;
}
else
{
//...
}
return;
}
//..
}
//...
}
// The VM corresponding to the IMvxModalIosView
public class MyProblematicModalVM : MvxViewModel
{
//...
public ICommand CloseCommand
{
get { return new MvxCommand(() => Close(this)); }
}
//...
}

No default Instance is registered and cannot be automatically determined for type 'EPiServer.Framework.Cache.IRequestCache

I have the following code
[Quartz.DisallowConcurrentExecutionAttribute()]
public class SearchIndexJob : IJob
{
private readonly ILog _Log = null;
private SearchManager _SearchManager;
public SearchIndexJob()
{
_Log = LogManager.GetLogger(GetType());
}
#region IJob Members
public void Execute(IJobExecutionContext context)
{
var container = new StructureMap.Container();
IServiceConfigurationProvider services = new StructureMapConfiguration(container);
var locator = new EPiServer.ServiceLocation.StructureMapServiceLocator(container);
var context2 = new EPiServer.ServiceLocation.ServiceConfigurationContext(HostType.WebApplication, services);
new Mediachase.Commerce.Initialization.CommerceInitialization().ConfigureContainer(context2);
container.Configure(ce =>
{
ce.For<IMarketService>().Use<MarketServiceDatabase>();
ce.For<IMarket>().Use<MarketImpl>();
ce.For<ICurrentMarket>().Singleton().Use<Mediachase.Commerce.Markets.CurrentMarketImpl>();
ce.For<ISynchronizedObjectInstanceCache>().Singleton().Use<EPiServer.Events.RemoteCacheSynchronization>();
ce.For<IObjectInstanceCache>().Use<HttpRuntimeCache>();
//ce.For<ITypeScannerLookup>().Use<FakeTypeScannerLookup>();
ce.For<IWarehouseRepository>().Singleton().Use<Mediachase.Commerce.Inventory.Database.WarehouseRepositoryDatabase>();
ce.For<IChangeNotificationQueueFactory>().Singleton().Use<CommerceChangeQueueFactory>();
ce.For<IPriceService>().Singleton().Use<PriceServiceDatabase>();
ce.For<IPriceDetailService>().Use<PriceDetailDatabase>();
ce.For<IWarehouseInventoryService>().Singleton().Use<WarehouseInventoryProxy>();
ce.For<IInventoryService>().Singleton().Use<InventoryServiceProvider>();
ce.For<IApplicationContext>().Use<FakeAppContext>();
ce.For<CatalogConfiguration>().Use(CatalogConfiguration.Instance);
ce.For<IRequiredMetaFieldCollection>().Singleton().Use<DefaultRequiredMetaFields>();
ce.For<MetaDataContext>().Singleton().Use(() => CatalogContext.MetaDataContext);
//ce.For<EventContext>().HybridHttpOrThreadLocalScoped().Use(eventContext);
ce.For<FrameworkContext>().Use(() => FrameworkContext.Current);
//ce.For<SqlContext>().Use(() => new SqlContext(BusinessFoundationConfigurationSection.Instance.Connection.Database));
ce.For<IChangeNotificationManager>().Singleton().Use<ChangeNotificationManager>();
////ce.For<Mediachase.Commerce.Catalog.ICatalogSystem>().Singleton().Use(() => Mediachase.Commerce.Catalog.CatalogContext.Current);
ce.For<IEventRegistry>().Use<EPiServer.Events.Clients.EventRegistry>();
ce.For<IEventBroker>().Use<FakeEventBroker>();
ce.For<Mediachase.Search.IndexBuilder>().Use<FakeIndexer>();
});
EPiServer.ServiceLocation.ServiceLocator.SetLocator(locator);
string applicationName = context.JobDetail.Description;
if (String.IsNullOrEmpty(applicationName) || applicationName == "all") // index all applications
{
AppDto dto = AppContext.Current.GetApplicationDto();
foreach (AppDto.ApplicationRow row in dto.Application)
{
IndexApplication(row.Name);
}
}
else
{
IndexApplication(applicationName);
}
}
#endregion
void IndexApplication(string applicationName)
{
_Log.Info(String.Format("Creating Search Manager for \"{0}\" Application.", applicationName));
_SearchManager = new SearchManager(applicationName);
_Log.Info("Created Search Manager.");
try
{
_SearchManager.SearchIndexMessage += new SearchIndexHandler(_SearchManager_SearchIndexMessage);
_SearchManager.BuildIndex(true);
}
catch (Exception ex)
{
_Log.Error("Search Manager Failed.", ex);
}
}
void _SearchManager_SearchIndexMessage(object source, SearchIndexEventArgs args)
{
_Log.Info(String.Format("Percent Complete: {0}%, {1}", Convert.ToInt32(args.CompletedPercentage), args.Message));
}
}
public class FakeEventBroker : IEventBroker
{
public bool Enabled { get; set; }
public System.Threading.Tasks.Task RaiseEventAsync(Guid eventId, Object parameter)
{
return null;
}
public event EventHandler<EventReceivedEventArgs> EventReceived;
public event EventHandler<EventMissedEventArgs> EventMissed;
}
public class FakeAppContext : IApplicationContext
{
public bool HasContentModelTypes { get; set; }
public bool DisableVersionSync { get; set; }
}
public class FakeIndexer : Mediachase.Search.IndexBuilder
{
public FakeIndexer() : base("","","")
{
}
}
and I get this error
"No default Instance is registered and cannot be automatically determined for type 'EPiServer.Framework.Cache.IRequestCache"
in this line " _SearchManager.BuildIndex(true);"
Any ideas?
It is hard to tell but I assume you need to register the IRequestCache in your container
I.e.
container.Configure(ce =>
{
ce.For<IMarketService>().Use<MarketServiceDatabase>();
ce.For<IMarket>().Use<MarketImpl>();
ce.For<IRequestCache>().Use<NoRequestCache>(); // or whatever implementation you need
...
}
Schedule Job is trying to Intialize Commerce, Most probably you will require to fix more then IRequestCache including DBContext, See an integration sample here. GIT Integration Sample

Best structure for Process Host of a WorkflowApplication - WF 4.5

I'm using Windows Workflow and am hosting a Process host in a normal C# Class file which is used by WCF classes (hosted in IIS). The host is not hosted as a service, it is a normal class. I've read lots of articles and looked at many example and created my class accordingly. It seems to work perfectly in a single user test situation but fails occassionally in multi-user situations. Please can you advise if I'm on the right track or doing something fundamentally wrong? Thanks.
Below is my code:
public sealed class PurchaseRequisitionProcessHost : IPurchaseRequisitionProcessHost
{
public event PurchaseRequisitionWfIdleEventHandler PurchaseRequisitionWf_Idle;
public delegate void PurchaseRequisitionWfIdleEventHandler(PurchaseRequisitionWorkflowApplicationIdleEventArgs e);
private static volatile PurchaseRequisitionProcessHost _instance = null;
private static object syncRoot = new Object();
private AutoResetEvent instanceUnloaded = null;
public static PurchaseRequisitionProcessHost Instance
{
get
{
if(_instance == null)
{
lock(syncRoot)
{
if(_instance == null)
{
_instance = new PurchaseRequisitionProcessHost();
}
}
}
return _instance;
}
}
private PurchaseRequisitionProcessHost()
{
}
protected void OnPurchaseRequisitionWf_Idle(PurchaseRequisitionWorkflowApplicationIdleEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
PurchaseRequisitionWfIdleEventHandler handler = PurchaseRequisitionWf_Idle;
// Event will be null if there are no subscribers
if(handler != null)
{
// Use the () operator to raise the event.
handler(e);
}
}
// executed when instance goes idle
public void OnIdle(WorkflowApplicationIdleEventArgs e)
{
PurchaseRequisitionWorkflowApplicationIdleEventArgs args = new PurchaseRequisitionWorkflowApplicationIdleEventArgs();
if(e.Bookmarks != null && e.Bookmarks.Any())
{
args.BookMarkName = e.Bookmarks[0].BookmarkName;
string prNumber = args.BookMarkName.Substring(args.BookMarkName.IndexOf("_") + 1);
prNumber = prNumber.Substring(0, prNumber.IndexOf("_"));
args.PrNumber = prNumber;
}
args.InstanceId = e.InstanceId;
PurchaseRequisitionWf_Idle(args);
}
public PersistableIdleAction OnIdleAndPersistable(WorkflowApplicationIdleEventArgs e)
{
return PersistableIdleAction.Unload;
}
public void OnWorkFlowUnload(WorkflowApplicationEventArgs e)
{
CleanUpSubscribedEvents();
instanceUnloaded.Set();
}
//// executed when instance has completed
public void OnWorkflowCompleted(WorkflowApplicationCompletedEventArgs e)
{
CleanUpSubscribedEvents();
instanceUnloaded.Set();
}
//// executed when instance has aborted
public void OnWorkflowAborted(WorkflowApplicationAbortedEventArgs e)
{
CleanUpSubscribedEvents();
instanceUnloaded.Set();
}
// executed when unhandled exception
public UnhandledExceptionAction OnWorkflowUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Helpers.Common.Common.logger.Error(string.Format("Unhandled WorkflowException for instance {0}", e.InstanceId), e.UnhandledException);
CleanUpSubscribedEvents();
instanceUnloaded.Set();
return UnhandledExceptionAction.Terminate;
}
private void CleanUpSubscribedEvents()
{
if(PurchaseRequisitionWf_Idle != null)
{
Delegate[] clientList = PurchaseRequisitionWf_Idle.GetInvocationList();
if(clientList != null && clientList.Any())
{
foreach(Delegate d in clientList)
{
PurchaseRequisitionWf_Idle -= (d as PurchaseRequisitionWfIdleEventHandler);
}
}
}
}
#region IPurchaseRequisitionProcessHost Members
public WorkflowApplication CreateAndRun(PurchaseRequisition pr, string uploadsPath)
{
return CreateAndRun(pr, uploadsPath, null);
}
// creates a workflow application, binds parameters, links extensions and run it
public WorkflowApplication CreateAndRun(PurchaseRequisition pr, string uploadsPath, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
WorkflowApplication instance = null;
try
{
instanceUnloaded = new AutoResetEvent(false);
using(var instanceStore = new DisposableStore())
{
// input parameters for the WF program
IDictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("PurchaseRequisition", pr);
inputs.Add("UploadsPath", uploadsPath);
instance = new WorkflowApplication(new PurchaseRequisitionProcessWorkflow(), inputs);
instance.InstanceStore = instanceStore.Store;
// Add the custom tracking participant
instance.Extensions.Add(new CustomTrackingParticipant());
instance.PersistableIdle += OnIdleAndPersistable;
instance.Completed += OnWorkflowCompleted;
instance.Unloaded += OnWorkFlowUnload;
instance.Aborted += OnWorkflowAborted;
instance.OnUnhandledException += OnWorkflowUnhandledException;
instance.Idle += OnIdle;
if(idleDelegate != null)
{
PurchaseRequisitionWf_Idle -= idleDelegate;
PurchaseRequisitionWf_Idle += idleDelegate;
}
// continue executing this instance
instance.Run();
instanceUnloaded.WaitOne();
}
}
catch(Exception ex)
{
Helpers.Common.Common.logger.Error(ex.Message, ex);
}
return instance;
}
public BookmarkResumptionResult SubmitApprovalDecision(Guid instanceId, string prNumber, string approvalSource, bool approved)
{
return SubmitApprovalDecision(instanceId, prNumber, approvalSource, approved, null);
}
public BookmarkResumptionResult SubmitApprovalDecision(Guid instanceId, string prNumber, string approvalSource, bool approved, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
BookmarkResumptionResult brr = BookmarkResumptionResult.NotReady;
WorkflowApplication instance = this.LoadInstance(instanceId, idleDelegate);
string bookmarkName = string.Format("Pr_{0}_waitingForApprovalFrom_{1}", prNumber, approvalSource);
if(instance != null && instance.GetBookmarks().Count > 0)
{
brr = instance.ResumeBookmark(bookmarkName, approved);
}
instanceUnloaded.WaitOne();
return brr;
}
public BookmarkResumptionResult ReceiptPrGoods(Guid instanceId, PurchaseRequisitionReceipt pr)
{
return ReceiptPrGoods(instanceId, pr, null);
}
public BookmarkResumptionResult ReceiptPrGoods(Guid instanceId, PurchaseRequisitionReceipt pr, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
BookmarkResumptionResult brr = BookmarkResumptionResult.NotReady;
WorkflowApplication instance = this.LoadInstance(instanceId, idleDelegate);
string bookmarkName = string.Format("Pr_{0}_waitingForReceiptOfGoods", pr.PrNumber);
if(instance != null && instance.GetBookmarks().Count > 0)
{
brr = instance.ResumeBookmark(bookmarkName, pr);
}
instanceUnloaded.WaitOne();
return brr;
}
public void UnsubscribePurchaseRequisitionWfIdleEvent(PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
PurchaseRequisitionWf_Idle -= idleDelegate;
}
#endregion
#region IProcessHost Members
// returns true if the instance is waiting (has pending bookmarks)
public bool IsInstanceWaiting(Guid instanceId)
{
bool isWaiting = false;
WorkflowApplication instance = LoadInstance(instanceId);
if(instance != null)
{
isWaiting = instance.GetBookmarks().Count > 0;
}
return isWaiting;
}
public WorkflowApplication LoadInstance(Guid instanceId)
{
return LoadInstance(instanceId, null);
}
// load and resume a workflow instance. If the instance is in memory,
// will return the version from memory. If not, will load it from the
// persistent store
public WorkflowApplication LoadInstance(Guid instanceId, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
WorkflowApplication instance = null;
try
{
instanceUnloaded = new AutoResetEvent(false);
using(var instanceStore = new DisposableStore())
{
instance = new WorkflowApplication(new PurchaseRequisitionProcessWorkflow());
WorkflowApplicationInstance wfAppInstance = WorkflowApplication.GetInstance(instanceId, instanceStore.Store);
// Add the custom tracking participant
instance.Extensions.Add(new CustomTrackingParticipant());
instance.PersistableIdle += OnIdleAndPersistable;
instance.Completed += OnWorkflowCompleted;
instance.Unloaded += OnWorkFlowUnload;
instance.Aborted += OnWorkflowAborted;
instance.OnUnhandledException += OnWorkflowUnhandledException;
instance.Idle += OnIdle;
if(idleDelegate != null)
{
PurchaseRequisitionWf_Idle -= idleDelegate;
PurchaseRequisitionWf_Idle += idleDelegate;
}
try
{
instance.Load(wfAppInstance);
}
catch(InstanceOwnerException)
{
}
}
}
catch(Exception ex)
{
Helpers.Common.Common.logger.Error(ex.Message, ex);
}
return instance;
}
#endregion
}
public class PurchaseRequisitionWorkflowApplicationIdleEventArgs : EventArgs
{
public string PrNumber
{
get;
set;
}
public Guid InstanceId
{
get;
set;
}
public string BookMarkName
{
get;
set;
}
}
public class DisposableStore : IDisposable
{
private string connectionString = Properties.Settings.Default.SqlPersistenceStoreDbConnectionString;
private SqlWorkflowInstanceStore _Store = null;
private InstanceHandle _handle = null;
public SqlWorkflowInstanceStore Store
{
get
{
if(_Store == null)
{
_Store = new SqlWorkflowInstanceStore(connectionString);
_Store.HostLockRenewalPeriod = TimeSpan.FromSeconds(5);
_Store.MaxConnectionRetries = 25;
_Store.RunnableInstancesDetectionPeriod = TimeSpan.FromSeconds(10);
_Store.InstanceCompletionAction = InstanceCompletionAction.DeleteAll;
_Store.InstanceLockedExceptionAction = InstanceLockedExceptionAction.BasicRetry;
_handle = _Store.CreateInstanceHandle();
CreateWorkflowOwnerCommand createOwnerCmd = new CreateWorkflowOwnerCommand();
InstanceView view = _Store.Execute(_handle, createOwnerCmd, TimeSpan.FromSeconds(5));
_Store.DefaultInstanceOwner = view.InstanceOwner;
}
return _Store;
}
}
public void Dispose()
{
_handle.Free();
_handle = _Store.CreateInstanceHandle(_Store.DefaultInstanceOwner);
try
{
_Store.Execute(_handle, new DeleteWorkflowOwnerCommand(), TimeSpan.FromSeconds(10));
}
catch(InstancePersistenceCommandException ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
catch(InstanceOwnerException ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
catch(OperationCanceledException ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
catch(Exception ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
finally
{
_handle.Free();
_Store.DefaultInstanceOwner = null;
_handle = null;
_Store = null;
}
}
}

Deadlock detection ordered lock implementation

I am trying to implement a mechanism where lock ordering is automatically checked and an exception is thrown when locks are acquired out of order at runtime to avoid deadlocks. A reference implementation is below. Please let me know if you see any issues with this implementation. Many thanks.
public class someResource
{
private OrderedLock lock1 = new OrderedLock(1);
private OrderedLock lock2 = new OrderedLock(2);
public void lockInOrder()
{
lock1.AcquireWriteLock();
lock2.AcquireWriteLock();
// do something
lock1.ReleaseWriteLock();
lock2.ReleaseWriteLock();
}
public void lockOutOfOrder()
{
lock2.AcquireReadLock();
lock1.AcquireReadLock(); // throws exception
// read something
lock2.ReleaseReadLock();
lock1.ReleaseReadLock();
}
}
public class OrderedLock : IDisposable
{
private static readonly ConcurrentDictionary<int, object> createdLocks = new ConcurrentDictionary<int, object>();
[ThreadStatic]
private static ISet<int> acquiredLocks;
private readonly ThreadLocal<int> refCount = new ThreadLocal<int>(false);
private readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private readonly int id;
/// <exception cref="InvalidOperationException">Duplicate identifier detected</exception>
public OrderedLock(int id)
{
if (!createdLocks.TryAdd(id, null))
{
throw new InvalidOperationException("Duplicate identifier detected");
}
this.id = id;
this.refCount.Value = 0;
}
public void AcquireReadLock()
{
this.CheckLockOrder();
this.locker.EnterReadLock();
}
public void AcquireWriteLock()
{
this.CheckLockOrder();
this.locker.EnterWriteLock();
}
public void ReleaseReadLock()
{
this.refCount.Value--;
this.locker.ExitReadLock();
if (this.refCount.Value == 0)
{
acquiredLocks.Remove(this.id);
}
}
public void ReleaseWriteLock()
{
this.refCount.Value--;
this.locker.ExitWriteLock();
if (this.refCount.Value == 0)
{
acquiredLocks.Remove(this.id);
}
}
public void Dispose()
{
while (this.locker.IsWriteLockHeld)
{
this.ReleaseWriteLock();
}
while (this.locker.IsReadLockHeld)
{
ReleaseReadLock();
}
this.locker.Dispose();
this.refCount.Dispose();
GC.SuppressFinalize(this);
}
/// <exception cref="InvalidOperationException">Invalid order of locking detected</exception>
private void CheckLockOrder()
{
if (acquiredLocks == null)
{
acquiredLocks = new HashSet<int>();
}
if (!acquiredLocks.Contains(this.id))
{
if (acquiredLocks.Any() && acquiredLocks.Max() > this.id)
{
throw new InvalidOperationException("Invalid order of locking detected");
}
acquiredLocks.Add(this.id);
}
this.refCount.Value++;
}
}

Categories