« July 2002 | Main | September 2002 »

August 31, 2002

Equals override

In the body of an override for Equals, the compiler appears to call the base class Object.Equals when comparing members of a class that has its own Equals override. Calling member1.Equals(obj2.member1) calls the override. Very puzzling.

 

The answer is that Object.Equals, operator == and operator != are three separate functions. The debugger’s command window turns a == b into a call to a.Equals(b) rather than operator ==(a,b). Likewise NUnit’s AssertEquals calls the Equals method. But in compiled C# code, using “==” will call the operator == method and not the Equals method. To remain sane, if you override Equals, do it in terms of overridden operator ==. And define operator != in terms of operator ==. Also remember to override GetHashCode.

August 30, 2002

Miscellaneous notes on cookies

1.       Cookies must have an expiration date to be accepted by the browser.

2.       The Response.Cookies collection is preserved across Server.Transfer calls.

3.       Yahoo expires their user id cookie in 10 years.

4.       Use Response.Redirect() to transfer to an arbitrary page from the server (requires a round trip).

August 29, 2002

Web Projects

1.       Much safer to create a new project and delete the old than it is to rename it.

2.       If Visual Studio won’t do the checkin, see if the change list can be submitted manually from p4win.

3.       Don’t forget to delete web projects in Intenet Information Systems manager after removing them from a project & from perforce.

4.       If a Web Application or Web Service project won’t load into Visual Studio, it may need to have its Directory properly configured in IIS. Check that Server Extensions are installed and Properties->directory->Application Settings->Create.

August 28, 2002

Enabling session state across multiple WebMethod calls.

To enable session state across multiple webmethod calls:

1.       On the web server side, set SessionState=true in the WebMethod attribute for each method.

2.       On the client proxy side, share a single proxy object across the calls and first set the CookieContainer property.

August 27, 2002

Threading Reference, Process, AppDomain, Monitor, WaitHandles, lock, delegate BeginInvoke, ThreadPool

Introduction

STA (Single Threaded Apartment) model requires that all access to an object be done by its creation thread and that any thread can create the object. Other threads need to marshal their calls to the object to the creation thread. The base class Control provides several methods (Invoke, BeginInvoke, EndInvoke) for this purpose.

Windows Forms uses STA because they’re built on the underlying Win32 objects that are STA. Calling into an STA object from an MTA thread (or the opposite) is allowed but usually SLOW.

Most of the .NET Framework is free threaded (each class defines its own threading issues and mechanisms are available to write compatible multi-threaded code).

Execution Units

Process

Win32 process.

System.Diagnostics.Process.GetCurrentProcess().Threads

Returns a collection of the threads in the current process.

AppDomain

Because of verifiable type-safety, provides process like isolation at lower performance cost.

Responsible for loading & unloading assemblies and security.

Every thread runs in a single AppDomain. Threads can transfer between AppDomains.

Objects can be copied between AppDomains or accessed by proxy.

System.AppDomain.CurrentDomain == System.Threading.Thread.GetDomain.

Most interesting methods: CreateDomain, SetData, GetData.

Thread

For a clean shutdown abort foreground threads and then Join them to wait for them to exit.

ProcessorAffinity is a cpu bitmask.

Deadlock Avoidance Rules:

1.       Acquire resources in fixed sequence.

2.       Release resources in the inverse sequence.

3.       Don’t wait for anything indefinitely.

4.       Acquire resources as late as possible.

5.       When an attempt to acquire a resource fails, release any dependent resources and reschedule.

 

new Thread(new ThreadStart(method))

                                               

ApartmentState

enum AppartmentState { MTA, STA, Unknown}.
Can only be set before the first call to unmanaged code.

ThreadPriority

enum ThreadPriority { AboveNormal, BelowNormal, Highest, Lowest, Normal }

ThreadState

[Flags] enum ThreadState { Aborted, AbortRequested, Background, Running, Stopped, StopRequested, Suspended, SuspendRequested, Unstarted, WaitSleepJoin }.

IsAlive

true if thread has been started and has not terminated normally or aborted.

IsBackground

Guarantees thread cleanup after all non-IsBackground threads have exited.

IsThreadPoolThread

Thread is managed by the ThreadPool. Name can’t be set.

Name

User friendly thread name property.

void Abort(object state)

Raises the ThreadAbortException in the thread on which it is invoked. The state will be available through the exception’s ExceptionState property. Abort can be canceled by calling void ResetAbort()

void Interrupt()

Interrupts a thread that is in the WaitSleepJoin state. Will wait until a thread is next in the WaitSleepJoin state before interrupting.

trueIfTerminated = bool Join(TimeSpan)

Waits for the thread to terminate or the timeout.

void Resume()

Resumes a thread that has been Suspend()’ed.

void Sleep(TimeSpan)

Blocks thread for a specified time. ThreadState adds WaitSleepJoin.

void Start()

Changes the ThreadState to Running. Begins executing the method bound to the ThreadStart delegate.

void Suspend()

Changes the ThreadState to Suspended.

AllocateNamedDataSlot

 

GetNamedDataSlot

 

Concurrency Control / Thread Safety

Monitor

Contains a reference to the thread that holds the lock, a ready queue of threads ready to obtain the lock, and a waiting queue of threads waiting for notification of a change in the state of the locked object.

Much faster than a Mutex (100x).

void Enter(object)

Any reference type can be used as the object to lock. For static methods use the classes Type reference. Locking only applies to clients using the exact same object reference.

Thread will wait if lock is already held by another thread.

Enter calls must be balanced by Exit calls on every thread. Place the corresponding Exit call in a finally block.

trueIfLockObtained = bool TryEnter(object, TimeSpan)

Tries for a fixed period of time to acquire a lock.

trueIfPulsed = bool Wait(object, TimeSpan, bool yieldContext)

Can only be called by the thread that holds the lock on object.

Lock is automatically released for the duration of the call.

If the timeout expires before enough Pulse or PulseAll calls occur to move the waiting thread from the waiting queue to the ready queue, it is moved to the back of the ready queue and will receive a false return value. The timeout is therefore not a tight control over how long it may take to regain control.

void Pulse(object)

Can only be called by the thread that holds the lock on object.

Moves the thread at the front of the waiting queue to the back of the ready queue.

void PulseAll(object)

Can only be called by the thread that holds the lock on object.

Moves all threads in the waiting queue to the back of the ready queue.

void Exit(object)

Decrements the lock count on object. Object must match what was passed to Enter. Thread continues to hold lock until all Enters have been matched by Exits.

When lock is released the thread at the front of the ready queue acquires the lock.

 [System.Runtime.CompilerServices.MethodImpl(Synchronized)]

Method attribute. Compiler puts a monitor on an entire method.

volatile

The compiler will not reorder access to the variable and always read its value from memory (not a register).

Volatile variables can’t be passed by reference.

Only class fields can be volatile.

WaitHandle

exitedContextBeforeWait = bool WaitAll(WaitHandle[], TimeSpan, bool yieldContext)

arrayIndexOrWaitTimeout = int WaitAny(WaitHandle[], TimeSpan, bool yieldContext)

trueIfSignalFalseIfTimeout = WaitOne(TimeSpan, bool yieldContext)

Mutex

Mutex(bool initiallyOwned, string nameOrNull, out bool createdNew)

void ReleaseMutex()

Must call same number of times as the number of wait calls.

Signaled when unowned and signaled for owner.

When a thread terminates, owned mutexes are released.

Works between processes. Much slower than Monitor, AutoResetEvent, and ManualResetEvent.

AutoResetEvent

AutoResetEvent(bool trueForInitiallySignaled)

trueIfSucceeded = bool Set()

When set, remains signaled until a thread is released by this WaitHandle, then automatically reset.

ManualResetEvent

ManualResetEvent(bool trueForInitiallySignaled)

trueIfSucceeded = bool Set()

trueIfSucceeded = bool Reset()

Interlocked

originalLocation = object CompareExchange(ref object location, object newValueIfLocationEqualsComparand, object comparand)

originalLocation = object Exchange(ref object location, object newValue)

decrementedValue = int Decrement(ref int location)

incrementedValue = int Increment(ref int location)

ContextBoundObject

Supports grouping objects by “context” and then enforcing concurrency constraints on them.

Access to an object derived from ContextBoundObject from a different context go through a proxy.

Non-ContextBoundObjects are called agile objects.

attribute can be placed on classes derived from ContextBoundObject to force all access from different contexts to go through a synchronization proxy.

High Level Constructs

lock (object) statement

Typically object is “this” for instances, “typeof(class)” for statics and all instances.

Equivalent to:

System.Threading.Monitor.Enter(object); try { statement } finally { System.Threading.Monitor.Exit(object); }

delegate

delegate myReturnType AnyDelegate(object anyArgs)

IAsyncResult BeginInvoke(object anyArgs, AsyncCallback, object state)

myReturnType EndInvoke([only non-in-only args,] IAsyncResult)

Every delegate class receives three compiler generated methods: Invoke, BeginInvoke, EndInvoke. Invoke is the composition of BeginInvoke & EndInvoke.

Implemented using ThreadPool(?).

ThreadPool

static bool System.Threading.ThreadPool.QueueUserWorkItem(System.Threading.WaitCallback callback, object state)

delegate void System.Threading.WaitCallback(object state)

This function and delegate work as follows:

1.       Construct a WaitCallback delegate from a callback method with a void(object state) signature.

2.       Queue the delegate and optionally pass in an object which the callback will receive.

3.       The callback method is invoked by a thread from the ThreadPool.

4.       Your on your own in terms of letting the caller know the work is done as well as aborting if it takes to long.

5.       The call can fail (in which case false is returned).

Unsafe version is faster because it doesn’t copy the stack to the callback which can be a security issue.

AutoResetEvent ev = new AutoResetEvent(false) ? WaitHandle

delegate void WaitOrTimerCallback(object, bool)

static RegisteredWaitHandle ThreadPool.RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, object, int, bool)

RegisteredWaitHandle.Unregister(WaitHandle)

AutoResetEvent.Set()

Each time the RegisteredWaitHandle is signaled, the WaitOrTimerCallback method will be invoked on a ThreadPool thread.

Multiple threads will be used to call the callback if the WaitHandle is signaled again before earlier invocations complete.

In additions, a timer is kept running and will also invoke the callback if it expires. The timer is reset by the WaitHandle being signaled.

RegisterWaitForSingleObject differs from QueueUserWorkItem in the following significant ways:

Work gets done by signaling a WaitHandle rather than by calling QueueUserWorkItem.

A timer runs automatically and also invokes the callback if it expires.

All ThreadPool threads are MTA.

Containers

Hashtable

Thread-safe for multiple readers and one writer.

ArrayList

Thread-safe for multiple readers.

Queue

NOT thread-safe.

Queue q = Queue.Synchronized(new Queue());

Makes a thread-safe wrapper for a queue.

2.5 times slower.

Returns a private derived queue wrapper class for a given queue.

Uses Monitor to guard the wrapped interface of the object returned by queue.SyncRoot (which is just the original queue reference).

ReaderWriterLock

ReaderWriterLock()

Creates an object used to guard a shared resource.

Logical implementation is a queue of waiting readers, a list of active readers, a queue of waiting writers and an active writer.

There’s no explicit connection to the shared resource.

void AcquireReaderLock(TimeSpan)

void AcquireWriterLock(TimeSpan)

Blocks until the requested lock is granted.

Throws ApplicationException on timeout.

void ReleaseReaderLock()

void ReleaseWriterLock()

Acquires must be balanced by releases.

Use a finally clause.

LockCookie UpgradeToWriterLock(TimeSpan)

void DowngradeFromWriterLock(ref LockCookie)

More efficient than releasing a reader lock and acquiring a writer lock.

The LockCookie restores the number of ReaderLocks held before the upgrade.

Don’t use a ReleaseLock() cookie.

LockCookie ReleaseLock()

void RestoreLock(ref LockCookie)

Efficient way to release all the locks held and easy to recover the same locks.

Don’t use an UpgradeToWriterLock() cookie.

bool IsReaderLockHeld

bool IsWriterLockHeld

For the curious.

int WriterSeqNum

bool AnyWriterSince(int)

Used to verify the validity of a cached resource value.

Must be holding a reader lock.

Thread Safety in .NET Libraries

All public static methods are thread-safe.

Common thread-safe classes:

·         System.Console

·         System.Text.Encoding

·         System.Diagnostics.Debug

·         System.Diagnostics.Trace

·         System.Diagnostics.PerformanceCounter

August 23, 2002

Web Services: Things learned & re-learned

Things learned & re-learned:

1.       Only the public fields of types returned by WebMethods get serialized (at least by default).

2.       Multi-dimensional arrays aren’t supported in types returned by WebMethods.

3.       To have state persist across web service requests, WebMethod(EnableSession=true) and save/access the state objects in the Session[] collection.

4.       Add the XmlInclude attribute to a web method that returns derived types. Requires namespace System.Xml.Serialization.

5.       IIS has the ability to use SQL or an in-memory session store mode. A bit of configuration is required for SQL J

6.       Turn off GET & POST protocols:

<webservices>
    <protocols>
        <remove name="HttpPost" />
        <remove name="HttpGet" />
    </protocols>
</webservices>

7.       Set web service namespace:

[WebServiceAttribute(Namespace = NamespaceConsts.AYS15Oct2002)]

8.       Set webmethod’s soap namespaces:

    [SoapDocumentMethodAttribute(

         NamespaceConsts.AYS15Oct2002 + "GetDisplayValue",

         RequestNamespace=NamespaceConsts.AYS15Oct2002,

         ResponseNamespace=NamespaceConsts.AYS15Oct2002 )]

9.       WebMethod signatures. If the method returns void and has multiple out parameters then the client stub incorrectly returns one of the out parameters. Avoid this by returning something other than void from the method.

10.    If a web service is denied file write access, a heavy handed solution is to edit machine.config to replace username=”machine” with username=”SYSTEM” in the processModel section.

August 05, 2002

XML Documentation implementation lags behind JavaDoc, data is there, tools are not.

dotNET’s JavaDoc equivalent is still very weak. Only the summary, remarks, returns and param tags appear to generate output via the standard tools: the object browser and intellisense. The Tools->Build Comment Web Pages… menu item generates web pages from the documentation tags, but they are so functionally limited as to be useless.

Specifically the see, seealso, and exception tags don’t generate links as expected. The generated .xml file does end up containing the tags.