In object-oriented programming an object is active when its state depends on clock. Usually an active object encapsulates a task that updates the object's state. To the outer world the object looks like a normal object with methods that can be called from outside. Implementation of such methods must have a certain synchronization mechanism with the encapsulated task in order to prevent object's state corruption.

A typical instance of an active object is an animation widget. The widget state changes with the time, while as an object it has all properties of a normal widget.

The task

Implement an active integrator object. The object has an input and output. The input can be set using the method Input. The input is a function of time. The output can be queried using the method Output. The object integrates its input over the time and the result becomes the object's output. So if the input is K(t) and the output is S, the object state S is changed to S + (K(t1) + K(t0)) * (t1 - t0) / 2, i.e. it integrates K using the trapeze method. Initially K is constant 0 and S is 0.

In order to test the object:

 set its input to sin (2π f t), where the frequency f=0.5Hz. The phase is irrelevant.
 wait 2s
 set the input to constant 0
 wait 0.5s
Verify that now the object's output is approximately 0 (the sine has the period of 2s). The accuracy of the result will depend on the OS scheduler time slicing and the accuracy of the clock.

#APPTYPE CONSOLE
 
#INCLUDE <Include\Windows.inc>
 
DIM Entity AS NEW Integrator(): SLEEP(2000) ' respawn and do the job
 
Entity.Relax(): SLEEP(500) ' get some rest
 
PRINT ">>> ", Entity.Yield(): DELETE Entity ' report and die
 
PAUSE
 
' ------------- End Program Code -------------
 
#DEFINE SpawnMutex CreateMutex(NULL, FALSE, "mutex")
#DEFINE LockMutex WaitForSingleObject(mutex, INFINITE)
#DEFINE UnlockMutex ReleaseMutex(mutex)
#DEFINE KillMutex CloseHandle(mutex)
 
CLASS Integrator
 
	PRIVATE:
 
	TYPE LARGE_INTEGER
		lowPart AS INTEGER
		highPart AS INTEGER
	END TYPE
 
	DIM dfreq AS DOUBLE, dlast AS DOUBLE, dnow AS DOUBLE, llint AS LARGE_INTEGER
	DIM dret0 AS DOUBLE, dret1 AS DOUBLE, mutex AS INTEGER, sum AS DOUBLE, thread AS INTEGER
 
	' --------------------------------------------
	SUB INITIALIZE()
		mutex = SpawnMutex
		QueryPerformanceFrequency(@llint)
		dfreq = LargeInt2Double(llint)
		QueryPerformanceCounter(@llint)
		dlast = LargeInt2Double(llint) / dfreq
		thread = FBSLTHREAD(ADDRESSOF Sampler)
		FBSLTHREADRESUME(thread)
	END SUB
	SUB TERMINATE()
		' nothing special
	END SUB
	' --------------------------------------------
 
	SUB Sampler()
		DO
			LockMutex
			SLEEP(5)
			QueryPerformanceCounter(@llint)
			dnow = LargeInt2Double(llint) / dfreq
			dret0 = Task(dlast): dret1 = Task(dnow)
			sum = sum + (dret1 + dret0) * (dnow - dlast) / 2
			dlast = dnow
			UnlockMutex
		LOOP
	END SUB
 
	FUNCTION LargeInt2Double(obj AS VARIANT) AS DOUBLE
		STATIC ret
		ret = obj.highPart
		IF obj.highPart < 0 THEN ret = ret + (2 ^ 32)
		ret = ret * 2 ^ 32
		ret = ret + obj.lowPart
		IF obj.lowPart < 0 THEN ret = ret + (2 ^ 32)
		RETURN ret
	END FUNCTION
 
	PUBLIC:
 
	METHOD Relax()
		LockMutex
		ADDRESSOF Task = ADDRESSOF Idle
		UnlockMutex
	END METHOD
 
	METHOD Yield() AS DOUBLE
		LockMutex
		Yield = sum
		FBSLTHREADKILL(thread)
		UnlockMutex
		KillMutex
	END METHOD
 
END CLASS
 
FUNCTION Idle(BYVAL t AS DOUBLE) AS DOUBLE
	RETURN 0.0
END FUNCTION
 
FUNCTION Task(BYVAL t AS DOUBLE) AS DOUBLE
	RETURN SIN(2 * PI * 0.5 * t)
END FUNCTION