best way to debounce in rx? - task-parallel-library

I'm using rx to observe an event.
There is only one subscriber with a lengthy operation.
The event may trigger again, when the previous operation is not finished.
how to drop this event ?
Here is what i'm searching: something like a SubscribeWithDebounceAsync method :
var observable = Observable.FromEventPattern<T>(obj, "OnSomeEvent");
observable.SubscribeWithDebounceAsync( ep => .... );

Your question sounds like you want to execute this operation any time the event is raised and you are not currently executing the operation. Your comments suggest you only want to execute the operation once.
The latter is simpler:
var observable = Observable.FromEventPattern<T>(...);
var disp = observable.Take(1).Subscribe(ep => ...);
For the former, I will assume that you intend to perform this long-running operation on another thread. If you don't, you will hold up the thread the Observable is raising messages on. SerialDisposable is your friend here:
<Extension()>
Public Function IgnoreWhileExecuting(Of T)(source As IObservable(Of T),
onNext As Func(Of T, Task),
onError As Action(Of Exception),
onCompleted As Action
) As IDisposable
'argument validation/error handling skipped for sample
Dim serial As New SerialDisposable
Dim syncNext As Action(Of T) = Nothing
'this function will be called for the initial and subsequent subscriptions
Dim subscribe As Func(Of IDisposable) =
Function()
Return source.Subscribe(syncNext,
onError,
onCompleted)
End Function
'this function will be "suspend" the subscription while executing
'the long-running operation, but it must return immediately to
syncNext = Sub(v)
'stop the current subscription to the source
serial.Disposable.Dispose()
'perform the long running operation and follow
'with resubscription. This will resubscribe regardless
'of if the task completes successfully or not
onNext(v).ContinueWith(Sub() serial.Disposable = subscribe())
End Sub
serial.Disposable = subscribe()
Return serial
End Function
As this will subscribe and unsubscribe, it generally assumes that you have a hot observable, like the event observable you are using in your question. You can test this out in a console app.
Sub Main()
Dim left = Observable.Interval(TimeSpan.FromMilliseconds(500))
Dim leftHot = left.Do(Sub(v) WriteTimestamped("Tick {0}", v)).Publish()
Dim f As New TaskFactory
Dim disp = leftHot _
.IgnoreWhileExecuting(Function(v)
Return f.StartNew(Sub(tparam)
WriteTimestamped("Before sleep {0}", tparam)
Thread.Sleep(2000)
WriteTimestamped("After sleep {0}", tparam)
End Sub,
v)
End Function,
Sub(ex) Console.WriteLine("Error in ignore: " & ex.ToString()),
Sub() Console.WriteLine("Completed from ignore"))
Dim con = leftHot.Connect()
Console.ReadKey()
disp.Dispose()
Console.ReadKey()
con.Dispose()
Console.ReadKey()
End Sub
Private Sub WriteTimestamped(ByVal format As String, ByVal arg As Object)
Console.WriteLine(Date.Now.ToString("HH:mm:ss.f") & " " & String.Format(format, arg))
End Sub
To see the difference between hot and cold, remove the Publish call and corresponding Connect and connection disposal and run the sample again.

Here's a different implementation:
public static class Extensions
{
public static IDisposable SubscribeWithDebounceAsync<T>(
this IObservable<T> source,
Action<T> longRunningTask)
{
var finished = new Subject<Unit>();
var debounced = source
.SkipUntil(finished)
.Take(1)
.Repeat()
.Subscribe(
t => Observable.Start(() =>
{
longRunningTask(t);
finished.OnNext(Unit.Default);
}));
finished.OnNext(Unit.Default);
return debounced;
}
}
The last finished.OnNext is there to get the process started, otherwise it'll stall forever on SkipUntil(finished).
EDIT: Made code more concise.

Related

VB6 Subscript Out fo Range - but this is an odd one because a mirror function is working fine

Thanks for reading.
I have built a VB6 DLL (VB_InterFace just for a name) that talks to a C# DLL (C#_Driver just for a name) that talks to a Bluetooth Device.
The Demo VB6 test app (VB_Demo just for a name) I created as stage one works fine, does what it is supposed to. It calls the VB_Interface and Opens and Closes the BTDevice. Additional functions also work fine.
However on placing the operational code from VB_Interface into another DLL that is the live operations DLL, Open works fine, but Close is throwing an error. "Variable not defined" when returning from the C#_Driver.
I just can't see why, the code is the same, the process is only marginally different. By this I mean ;
In the VB_Demo I have two buttons "Open" "Close" and when I click on these I get the feedback that I expect from the BTDevice.
Private Sub btnOpenPort_Click()
'MsgBox steps(0)
ReDim steps(5)
Dim rc As HF4000_ResultCodes
'rc = driver.OpenSerial(cmbPorts.Text)
If driver.OpenSerial(cmbPorts.Text) = True Then
Private Sub btnClosePort_Click()
Dim rc As HF4000_ResultCodes
If driver.CloseSerial("COM4") = True Then
However in the live DLL it just executes the same functions internally without being initiated by a button click.
' See IScanDevice documentation.
' #see IScanDevice#OpenDevice
Private Function IScanDevice_OpenDevice() As Scanning.Scan_ResultCodes
(truncated slightly)
50 If driver.OpenSerial("COM4") = True Then
rc = READY
MsgBox "Connected to the device successfully."
' See IScanDevice documentation.
' #see IScanDevice#CloseDevice
Private Function IScanDevice_CloseDevice() As Scanning.Scan_ResultCodes
(truncated slightly)
50 If driver.CloseSerial("COM4") = True Then
60 rc = READY
70 IScanDevice_CloseDevice = Scan_Success
clsDriver.cls
Public Event OnStateChanged(newState As String)
Public Event OnDataUpdated()
Dim WithEvents CSharpInteropServiceEvents As CSharpInteropService.LibraryInvoke
Dim load As New LibraryInvoke
Private Sub Class_Initialize()
Set CSharpInteropServiceEvents = load
End Sub
Private Sub CSharpInteropServiceEvents_MessageEvent(ByVal newState As String)
If newState = "OpenForm1" Then
' FormDummy2.Show ' Not required
End If
If State <> newState Then
State = newState
RaiseEvent OnStateChanged(State)
GetDriverData
End If
End Sub
Private Function BluetoothTestInvoke(load, functionName, param)
BluetoothTestInvoke = load.GenericInvoke("BluetoothTest.dll", "BluetoothTest.Class1", functionName, param)
End Function
Function OpenSerial(portNumber) ' "COM4"
Dim param(0) As Variant
Dim retorno As Variant
param(0) = portNumber
retorno = BluetoothTestInvoke(load, "OpenSerial", param)
OpenSerial = retorno(0) <<<<<<< Works fine returns TRUE
End Function
Function CloseSerial(portNumber) ' "COM4"
Dim param(0) As Variant
Dim retorno As Variant
param(0) = portNumber
retorno = BluetoothTestInvoke(load, "CloseSerial", param)
CloseSerial = retorno(0) <<<<<<<<< "Error Subscript Out of Range"
End Function
What I have discovered is this - and I guess this is the reason why the Close is not working. The question is why is this situation occurring ...
When driver.OpenSerial executes, it hits > Function OpenSerial
Within Function OpenSerial it executes BluetoothTestInvoke where "load" is "CSharpInteropService.LibraryInvoke"
From there it moves to - Sub CSharpInteropServiceEvents_MessageEvent
.. and everything is fine.
However when I then execute driver.CloseSerial after that, it hits > Function CloseSerial
Within Function OpenSerial it executes BluetoothTestInvoke where "load" is "CSharpInteropService.LibraryInvoke"
Now here it "should" move to - Sub CSharpInteropServiceEvents_MessageEvent
However No, it just drops to the next line which is CloseSerial = retorno(0)
and this is where I get the "Subscript out of range" error for retorno(0)
For some reason in the CloseSerial it is not invoking "load"
BluetoothTestInvoke(load, "CloseSerial", param)
Thoughts and suggestions much appreciated.
UPDATE
Quite right, one should never assume anything.
On the tips I started digging deeper into the C# Library. It turns out the "param" value that is the Bluetooth port is passed into the CloseSerial call, and from there is is passed around within the external C# library dll. At one stage it is reset so the port number that should be handled is lost, thus it doesn't close but specifically the "expected" data was not returned to the calling app.
param(0) = portNumber
retorno = BluetoothTestInvoke(load, "CloseSerial", param) << param was being reset in the external library.

Need to pass object and operation in a function that executes it

I need to pass an object and its operation in a function so that each time I can call the function only and save me to write same steps for all the objects like validating the object before performing an operation. Similar way to a Register User Function in QTP/UFT.
However, Testcomplete doesn't have this feature (atleast under my knowledge, would be happy to know if there is)
This is my code that I am trying but unable to:
Call OpenPageorTab("Aliases.Admin.wndMain.toolStrip", ".Visible")
Function OpenPageorTab(obj, method)
'if obj.Exists then
execute a = obj&method
delay(1000)
OpenPageorTab = True
'Else
log.Error "Unable to find the object"
OpenPageorTab = False
'End if
using if condition as i was passing object earlier instead of string
It fails at "execute" statement and gives me VbScript runtime error when executing this statement.
my question is two fold -
How do I pass objects and its operation in a function and execute it
Also is it possible to pass an object it self instead of string for ex:
obtoolbar = "Aliases.Admin.wndMain.toolStrip"
Call OpenPageorTab(obtoolbar, ".Visible")
Appreciate any help or direction on this issue
EDIT 1
I am somewhere close to an answer however not accurately. I am able to pass the object as string - Check the code below
Call OpenPageorTab("Aliases.Admin.wndMain.toolStrip", ".Click")
Function OpenPageorTab(obj, method)
' if obj.Exists then
eobj = "" & obj & method
execute (eobj)
delay(1000)
OpenPageorTab = True
' Else
log.Error "Unable to find the object"
OpenPageorTab = False
' End if
End Function
However I still need to pass the object something like
Set oToolStrip = Aliases.Admin.wndMain.toolStrip
Call OpenPageorTab(oToolStrip, ".Click")
This is something that I'm unable to do.
EDIT 2
I have already got the answer to this problem and have posted the solution. That being said, is there any way that Function can be utilized as a method ?
Here an example of how to reference a function and pass parameteers to it, including objects.
Const forReading = 1, forWriting = 2, forAppending = 8, CreateFile = True
Set my_obj = CreateObject("Scripting.FileSystemObject").OpenTextFile("c:\temp\test.txt", forWriting, CreateFile)
Function my_function(my_obj, method, text)
command = "my_obj." & method & " """ & text & """"
ExecuteGlobal command
End Function
'make a reference to our function
Set proc = GetRef("my_function")
'and call it with parameters, the first being the method invoked
Call proc(my_obj, "WriteLine", "testing")
'cleanup'
my_obj.Close
Set my_obj = Nothing
I was able to finally formulate the solution the below function can work as makeshift register function in TestComplete
Sub test
'Set the Object
Set pToolStrip = Aliases.Admin.wndMain.toolStrip.Button("User Admin")
Call GenericOperationFunc(pToolStrip, ".Click", "N")
'if you want to perform an operation which return a value
b = GenericOperationFunc(Aliases.Admin.wndPopup.Child(2), ".Caption", "Y")
End Sub
Public Function GenericOperationFunc(obj, method, Return)
GenericOperationFunc = False
on error resume next
if obj.Exists then
if Ret = "Y" then
eobj = "NewText="&"obj" & method
execute (eobj)
GenericOperationFunc = NewText
Delay(500)
Else
eobj = "obj" & method
execute (eobj)
delay(1000)
GenericOperationFunc = True
End if
Else
log.Error "Unable to find the object"
GenericOperationFunc = False
End if
End Function
'log.error, delay, aliases, ptoolstrip(object) are testcomplete specific

VB.net anonymous type has incorrect property casing from AJAX call

We have noticed that sometimes from the results of an AJAX call to a controller action that the case of the JSON result is incorrect. The returned case will actually change if we rebuild our solution and try the exact same call. In the following case, the key's case has been correct for over a year until now when it has decided to start randomly changing depending on some seemingly random circumstances.
As you can see in the picture above, the key for the JSON result is lowercase "success". However when I view the results in Chrome's console, it is an uppercase "Success". This is causing our JavaScript to fail since it is checking for the lowercase version.
What is causing this? And more importantly, how do we stop this?
vb.net is case-insensitive as opposed to C# which is case-sensitive. This means that the compiler will generate only one class (from the first instance) for each of the following anonymous types:
Dim a = New With {.success = True} 'Compiler generate a class based on this type
Dim b = New With {.Success = True} 'Same type as `a`
Dim c = New With {.sUcCeSs = True} 'Same type as `a`
Debug.WriteLine(a.GetType().Name)
Debug.WriteLine(b.GetType().Name)
Debug.WriteLine(c.GetType().Name)
VB$AnonymousType_0'1
VB$AnonymousType_0'1
VB$AnonymousType_0'1
Here's how the complied code looks like when compiled back to vb.net:
<DebuggerDisplay("success={success}"), CompilerGenerated> _
Friend NotInheritable Class VB$AnonymousType_0(Of T0)
' Methods
<DebuggerNonUserCode> _
Public Sub New(ByVal success As T0)
Me.$success = success
End Sub
<DebuggerNonUserCode> _
Public Overrides Function ToString() As String
Dim builder As New StringBuilder
builder.Append("{ ")
builder.AppendFormat("{0} = {1} ", "success", Me.$success)
builder.Append("}")
Return builder.ToString
End Function
Public Property success As T0
<DebuggerNonUserCode> _
Get
Return Me.$success
End Get
<DebuggerNonUserCode> _
Set(ByVal Value As T0)
Me.$success = Value
End Set
End Property
Private $success As T0
End Class

VBScript determine which function is running

Is it possible in VBScript to determine the name of the function currently executing?
In .NET, you could do:
MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);
In the past, I build a callstack viewer to see the performance of each function that is called. This needs one extra line of VBS code per function/sub and some overhead during runtime of course because of the extra code.
bottom - up:
Function DoSomething(a, b, c)
dim registerFunctionObj : Set registerFunctionObj = [new RegisterFunction]("DoSomething")
' other code
End Function
Whenever the function is called, it creates a new instance of the RegisterFunction object. When the function exits, the registerFunctionObj variable goes out of scope automatically, calling the Class_Terminate sub of the instance.
[new RegisterFunction] is just a function that return a registerFunction instance:
Function [new RegisterFunction](funcName)
Set [new RegisterFunction] = new cls_RegisterFunction
[new RegisterFunction].FunctionName = funcName
Set [new RegisterFunction].CallStackViewer = CallStackViewer
End function
Class cls_RegisterFunction
Private functionName_, startTime_, callStackViewer_, endTime_
Private Sub Class_Initialize
startTime_ = now
callStackViewer_.LogInitialize me
End Sub
Public Property Let FunctionName(fName)
functionName_ = fName
End Property
Public Property Set CallStackViewer(byRef csv)
Set callStackViewer_ = csv
End Property
Private Sub Class_Terminate
endTime_ = now
callStackViewer_.LogTerminate me
End Sub
End Class
The CallStackViewer instance is a singleton instance of the a CallStackViewer class, but you can make it a part of your project, so you retrieve it through you global project class:
Private PRIV_callStackViewer
Public Function CallStackViewer()
If not IsObject(PRIV_callStackViewer) Then
Set PRIV_callStackViewer = new cls_CallStackViewer
End If
Set CallStackViewer = PRIV_callStackViewer
End Function
Class cls_CallStackViewer
Public Sub Class_Initialize
' Here you can retrieve all function libraries (as text file) extract the
' function name, the file they are in and the linenumber
' Put them in a dictionary or a custom object
End Sub
Public Sub LogInitialize(byref registerFunction)
' Here you can push the function on a stack (use a standard dotnet list>stack for it),
' log the starttime to a log object or handle custom breakpoints
End Sub
Public Sub LogTerminate(byref registerFunction)
' Here you can pop the function from a stack, log the endtime to a log
' object or handle custom breakpoints
End Sub
End Class
Disclaimer: The code in here is pure demo code created on the fly. It lacks functionality and is only here to explain the concept. It could contain errors and is not complete.
The only thing you need is one line of code per function and your own imagination to expand it.
No, but you can easily implement it
dim module_name
sub sub1
module_name = "sub1"
wscript.echo "i'm " & module_name
'do something
end sub
function function1
module_name = "function1"
wscript.echo "i'm " & module_name
function1 = "something"
end function
In case of recursion you could also remember the level you'r in so that you can get out if getting too deep.

Windows Service HTTPListener Memory Issue

Im a complete novice to the "best practices" etc of writing in any code.
I tend to just write it an if it works, why fix it.
Well, this way of working is landing me in some hot water. I am writing a simple windows service to server a single webpage. (This service will be incorperated in to another project which monitors the services and some folders on a group of servers.)
My problem is that whenever a request is recieved, the memory usage jumps up by a few K per request and keeps qoing up on every request.
Now ive found that by putting GC.Collect in the mix it stops at a certain number but im sure its not meant to be used this way. I was wondering if i am missing something or not doing something i should to free up memory.
Here is the code:
Public Class SimpleWebService : Inherits ServiceBase
'Set the values for the different event log types.
Public Const EVENT_ERROR As Integer = 1
Public Const EVENT_WARNING As Integer = 2
Public Const EVENT_INFORMATION As Integer = 4
Public listenerThread As Thread
Dim HTTPListner As HttpListener
Dim blnKeepAlive As Boolean = True
Shared Sub Main()
Dim ServicesToRun As ServiceBase()
ServicesToRun = New ServiceBase() {New SimpleWebService()}
ServiceBase.Run(ServicesToRun)
End Sub
Protected Overrides Sub OnStart(ByVal args As String())
If Not HttpListener.IsSupported Then
CreateEventLogEntry("Windows XP SP2, Server 2003, or higher is required to " & "use the HttpListener class.")
Me.Stop()
End If
Try
listenerThread = New Thread(AddressOf ListenForConnections)
listenerThread.Start()
Catch ex As Exception
CreateEventLogEntry(ex.Message)
End Try
End Sub
Protected Overrides Sub OnStop()
blnKeepAlive = False
End Sub
Private Sub CreateEventLogEntry(ByRef strEventContent As String)
Dim sSource As String
Dim sLog As String
sSource = "Service1"
sLog = "Application"
If Not EventLog.SourceExists(sSource) Then
EventLog.CreateEventSource(sSource, sLog)
End If
Dim ELog As New EventLog(sLog, ".", sSource)
ELog.WriteEntry(strEventContent)
End Sub
Public Sub ListenForConnections()
HTTPListner = New HttpListener
HTTPListner.Prefixes.Add("http://*:1986/")
HTTPListner.Start()
Do While blnKeepAlive
Dim ctx As HttpListenerContext = HTTPListner.GetContext()
Dim HandlerThread As Thread = New Thread(AddressOf ProcessRequest)
HandlerThread.Start(ctx)
HandlerThread = Nothing
Loop
HTTPListner.Stop()
End Sub
Private Sub ProcessRequest(ByVal ctx As HttpListenerContext)
Dim sb As StringBuilder = New StringBuilder
sb.Append("<html><body><h1>Test My Service</h1>")
sb.Append("</body></html>")
Dim buffer() As Byte = Encoding.UTF8.GetBytes(sb.ToString)
ctx.Response.ContentLength64 = buffer.Length
ctx.Response.OutputStream.Write(buffer, 0, buffer.Length)
ctx.Response.OutputStream.Close()
ctx.Response.Close()
sb = Nothing
buffer = Nothing
ctx = Nothing
'This line seems to keep the mem leak down
'System.GC.Collect()
End Sub
End Class
Please feel free to critisise and tear the code apart but please BE KIND. I have admitted I dont tend to follow the best practice when it comes to coding.
You are right, you should not be doing this. Remove the Collect() call and let it run for a week. Any decent .NET book will talk about how the garbage collector works and how it does not immediately release memory when you set an object to Nothing. It doesn't kick in until you've consumed somewhere between 2 and 8 megabytes. This is not a leak, merely effective use of a plentiful resource.
You use a new thread for each individual connection, that's pretty expensive and scales very poorly when you get a lot of connections. Consider using ThreadPool.QueueUserWorkItem instead. Threadpool threads are very cheap and their allocation and execution is well controlled by the threadpool manager.

Resources