Site moved to www.ohadsoft.com

May 24, 2015

Be there or be square :D

In C++, even a thread is not thread-safe (or: why you should use tasks part 2)

April 12, 2015

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2015/04/in-c-even-a-thread-is-not-thread-safe-or-why-you-should-use-tasks-part-2/

 

Consider the following (contrived) code:

using namespace std;

void work()
{
    this_thread::sleep_for(chrono::seconds(2));
}

void wait(thread* t)
{
    t->join();
}

int main()
{
    thread worker(work);
    thread waiter1(wait, &worker);
    thread waiter2(wait, &worker);

    waiter1.join();
    waiter2.join();

    return 0;
}

The intent of the code is quite clear – we spawn a worker thread and have two threads wait for it to finish. Let’s assume that the sleep is long enough so that both threads try and join while the worker is still running (otherwise the thread stops being joinable and calling join throws). Even under that assumption, the code’s behavior is not defined (UB). The reason is in the docs:

Note that any operations on the thread object itself are not synchronized (unlike the operations within the thread it represents).

In other words, the std::thread object itself is not thread safe, so we can’t call its methods concurrently (which is what we’re doing in waiter1 and waiter2). We’ll have to do something like this:

using namespace std;

mutex m;

void work()
{
     this_thread::sleep_for(chrono::seconds(2));
}

void wait(thread* t)
{
    lock_guard<std::mutex> lock(m);
    if (t->joinable())
    {
        t->join();
    }
}

int _tmain()
{
    thread worker(work);
    thread waiter1(wait, &worker);
    thread waiter2(wait, &worker);

    waiter1.join();
    waiter2.join();

    return 0;
}

Of course, were you using tasks, you wouldn’t have needed to concern yourself with such trivialities:

using namespace std;
using namespace concurrency;

auto worker = create_task([]
{
    this_thread::sleep_for(chrono::seconds(2));
});

auto waiter1 = create_task([&worker]
{
    worker.wait();
});

auto waiter2 = create_task([&worker]
{
     worker.wait();
});

waiter1.wait(); //works
waiter2.wait(); //great

I hope this has taken you one step closer to ditching bare threads (if you haven’t already). If it didn’t, be sure to check out (Not) using std::thread by Andrzej Krzemieński (his blog is great all around, so I recommend you check it out anyway).

Reminder:

The case of the crashing std::thread destructor and why you should use tasks instead of threads

January 27, 2015

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2015/01/the-case-of-the-crashing-stdthread-destructor-and-why-you-should-use-tasks-instead-of-threads/

 

I recently encountered an interesting crash in our iOS application at work. Here’s the relevant stack trace section:

…
libc++abi.dylib <strong>std::terminate</strong>()
libc++.1.dylib  <strong>std::__1::thread::~thread</strong>()
…

Luckily, the documentation for std::~thread provides us with the root cause:
If *this has an associated thread (joinable() == true), std::terminate() is called.

Specifically, the joinable() documentation states:
A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable.

Seeing as the thread in question was indeed never joined (or detached) we were hit by this error. Now, this issue can be solved by joining with or detaching from the thread, but I think that would be the wrong lesson to learn here.

This is just one example of how things are more difficult with threads than they are with tasks. Practically any imaginable scenario is made easier and less error-prone with tasks. Just as tasks were a paradigm shift in .NET, they should be in C++11 – only manipulate threads directly if you must!

2014 in review

December 29, 2014

The WordPress.com stats helper monkeys prepared a 2014 annual report for this blog.

Here’s an excerpt:

The concert hall at the Sydney Opera House holds 2,700 people. This blog was viewed about 16,000 times in 2014. If it were a concert at Sydney Opera House, it would take about 6 sold-out performances for that many people to see it.

Click here to see the complete report.

Posting forms with Knockout to dynamic Sammy routes

December 27, 2014

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2014/12/posting-forms-with-knockout-to-dynamic-sammy-routes/

 

Single-page applications (SPAs) are the standard these days, and two aspects are typically considered for their development:

  1. Data binding the view (HTML) with the model (Javascript)
  2. Navigating between logical “pages” without reloading.

There are many libraries that handle both needs, and I went with Knockout for (1) and Sammy for (2) – mostly because I was already familiar with them (they are both excellent).

The way Sammy works is by defining “routes” (typically hash tag routes for SPAs), which are basically URLs that trigger JS methods. For example, you might define the following route:

get('#/:name', function() {
    alert(this.params['name']);
});

When the URL changes to mysite.com/#/Ohad, an alert will pop up saying “Ohad”. Now, suppose we have a form we want to “submit” to the route above Sammy route:

<form
data-bind="submit:function(){window.location='#/'+name();}">
 <input type="text" data-bind="value: name"></input>
 <button type="submit">Submit</button>
</form>

The input’s data-bind attribute binds the input’s value to our view model’s name observable. The form’s data-bind attribute binds the form submission event to a URL redirection for the desired Sammy route. For more information about Knockout bindings, visit http://learn.knockoutjs.com.

The code above works, but not exactly as expected. See, when no action attribute is specified on the form element, the browser assumes the current URL without hashtags is the submission URL, and so right after the URL is redirected to mysite.com/#/MyName it is reverted to just mysite.com by the browser.

This messed up my hash tag navigation model, and nothing I tried prevented the browser from reverting the address (canceling the form submission event, returning false from the handler method, etc). And then it hit me – why fight the browser?

<form
data-bind="attr: { action: '#/'+name() }">
 <input type="text" data-bind="value: name"></input>
 <button type="submit">Submit</button>
</form>

See what we did here? Instead of using knockout to circumvent the normal form submission flow, I go with it. Whereas Knockout’s submit binding tries to cancel the browser’s submission event and replace it with the supplied method, here the submission flow is completely standard. We just need to make sure the action attribute points to the right place, and voila – hash tag history is preserved.

BTW, you may wonder why I insisted to go with a form, where in truth no “real” submission takes place here. Indeed, a regular div with a regular button would not have had this issue to begin with!

Well, the answer is a bit silly. I could not find any reliable cross-browser method to intercept the [enter] key on the input in order to trigger submission (everything is either deprecated or not globally supported). And frankly, even if I did, it would be a hack – the browser should handle such logic. Plus, I enjoyed the challenge :)

Static definitions in header files may cause malloc errors on application teardown

December 12, 2014

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2014/12/static-definitions-in-header-files-may-cause-malloc-errors-on-application-teardown/

 

I recently encountered the following error in the shutdown procedure of XCTest (we’re using Kiwi which is based on it):

Malloc Error: pointer being freed was not allocated

Here’s how the stack looked like (truncated to section of interest):

(lldb) bt

frame #4: 0x00e12ae7 OurProduct`std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> >::~basic_string(this=0x042a2058) + 103 at string:2093

frame #5: 0x00e10cb7 OurProductI`std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> >::~basic_string(this=0x042a2058) + 23 at string:2090

frame #6: 0x0c94a7ac libsystem_sim_c.dylib`__cxa_finalize_ranges + 315

frame #7: 0x0c94a832 libsystem_sim_c.dylib`__cxa_finalize + 59

frame #8: 0x0c94ab55 libsystem_sim_c.dylib`exit + 57

frame #9: 0x20117684 XCTest`+[XCTestProbe exitTests:] + 57

As you can see, XCTest was executing its shutdown procedure, and during this finalization phase the destructors of static instances were executed. It was somewhat vexing that a static destructor would try to free up a pointer that was never allocated – after all, statics are allocated only once when the process starts up. And if some static was not defined (or defined multiple times), a linker error should have been thrown. How could this situation come to be?

Failing to answer that question armed with the data above alone, I proceeded to attempt and locate the faulty string in our code. I had hoped I could spot something unusual about it, that would hopefully hint me at the root cause of the failure.

(lldb) frame select 4
(lldb) frame variable *this

(std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> >) *this = {

__r_ = {

std::__1::__libcpp_compressed_pair_imp<std::__1::basic_string<char16_t, std::__1::char_traits<char16_t>, std::__1::allocator<char16_t> >::__rep, std::__1::allocator<char16_t> > = {

__first_ = {

= {

__l = (__cap_ = 17, __size_ = 14, __data_ = u”Helvetica Neue“)
__s = {

The string looked innocent enough. However, but upon finding it in our code (thankfully the literal appeared as is, and only once), I noticed it was defined (initialized) in the header. Statics should be declared in the header – never defined. Code such as  Bar::foo = “Helvetica Neue” belongs in the cpp file. For more information see http://stackoverflow.com/questions/185844/initializing-private-static-members.

And indeed, moving the definition to the cpp resolved the issue. Now, the keen reader will notice that a linker error should have been thrown, rather than the runtime exception I encountered. I’m not exactly clear on why the former was not the case, but the prevailing theory entails a linker optimization that allocated only once for multiple compilation units, which backfired when the shutdown sequence tried to free all occurrences separately (or one occurrence that happened to not be the one that was initialized).

If you have a better explanation, I’ll be happy to hear it. In the meantime, mind your statics :)

Debugging unobserved concurrency runtime task exceptions

December 4, 2014

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2014/12/debugging-unobserved-concurrency-runtime-task-exceptions/

 

The C++ Concurrency runtime (AKA PPLX) is the unmanaged answer to the Task Parallel Library (TPL), and it works surprisingly well. It is even cross platform by way of the C++ Rest SDK (codename Casablanca).

I work at Microsoft and in our group we are making extensive use of this library (we develop an iOS application). Recently, I encountered an interesting crash due to an unobserved exception. An unobserved exception is basically an exception thrown from a task, which no other entity (be it the caller or some later continuation) observed (typically by calling task::wait or task::get). In PPLX, such exceptions crash the process (in .NET they used to, and still may depending on configuration).

When an unobserved PPLX exception occurs, the debugger will break in the following location inside pplxtasks.h:

// If you are trapped here, it means an exception thrown in task chain didn’t get handled.
// Please add task-based continuation to handle all exceptions coming from tasks.
// this->_M_stackTrace keeps the creation callstack of the task generates this exception.
_REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); // <– debugger will break here

Your mileage may vary, but I wasn’t able to inspect the _M_stackTrace variable in XCode’s debugger.

However, using the lldb console (you can bring it up with ⌘+Shift+C) I was able to inspect its value:

(lldb) expr _M_stackTrace

Here is a sample output:

(pplx::details::_TaskCreationCallstack) $1 = {
_M_SingleFrame = 0x00c0935d
_M_frames = size=0 {}
}

In this case, the frame of interest is stored in _M_SingleFrame (otherwise the list of frames would have been stored in _M_frames and _M_SingleFrame would have been null). Of course, “0x00c0935d” is not a terribly useful piece of data for root cause analysis, and I wasn’t even sure what that address represented! Fortunately, following macro in the same header clarified that mystery:

#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress())

So now we know it is a return address, pointing to the code creating the offending exception. Fortunately, lldb can resolve that address to source:

(lldb) so l -a 0x00c0935d

Sample output:

/Users/ohads/Library/Developer/Xcode/DerivedData/ios-gdhrlqjldqgqktgtpdnpssahaqme/Build/Products/OurApp`pplx::task<void> pplx::task_from_exception<void, std::exception>(std::exception, pplx::task_options const&) + 62 at …

It’s not perfect, but it should be enough to narrow the search down (in this case, there was only one place in our code using task_from_exception).

Should you use std::string, std::u16string, or std::u32string?

November 17, 2014

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2014/11/should-you-use-stdstring-stdu16string-or-stdu32string/

 

C++11 introduced a couple of new string classes on top of std::string:

  1. u16string
  2. u32string

“Finally”, you must think, “C++ has addressed the sorry state of Unicode development in portable code! All I have to do is choose one of these classes and I’m all set!”.

Well, you’d might want to rethink that. To see why, let’s take a look at some definitions:

typedef basic_string<char> string;

typedef basic_string<char16_t> u16string;

typedef basic_string<char32_t> u32string;

As you can see, they all use the same exact template class. In other words, there is nothing Unicode-aware, or anything special at all for that matter, with the new classes. You don’t get “Unicode for free” or anything like that. We do see however an important difference between them – each class uses a different type as an underlying “character”.

Why do I say “character” with double quotes? Well, when used correctly, these underlying data types should actually represent code units (minimal Unicode encoding blocks) – not characters! For example, suppose you have a UTF-8 encoded std::string containing the Hebrew word “שלום”. Since Hebrew requires two bytes per character, the string will actually contain 8 char “characters” – not 4!

And this is not only true for variable length encoding such as UTF-8 (and indeed, UTF-16). Suppose your UTF-32 encoded std::u32string contains the grapheme cluster (what we normally think of as a “character”) ў. That cluster is actually a combination of the Cyrillic у character with the Breve diacritic (which is a combining code point), so your string will actually contain 2 char32_t “characters” – not 1!

In other words, these strings should really be thought of as sequences of bytes, where each string type is more suitable for a different Unicode encoding:

  • std::string is suitable for UTF-8
  • std::u16string is suitable for UTF-16
  • std::u32string is suitable for UTF-32

Unfortunately, after all this talk we’re back to square one – what string class should we use? Well, since we now understand this is a question of encoding, the question becomes what encoding we should use. Fortunately, even though this is somewhat of a religious war, “the internet” has all but declared UTF-8 as the winner. Here’s what renowned Perl/Unicode expert Tom Christiansen had to say about UTF-16 (emphasis mine):

I yesterday just found a bug in the Java core String class’s equalsIgnoreCase method (also others in the string class) that would never have been there had Java used either UTF-8 or UTF-32. There are millions of these sleeping bombshells in any code that uses UTF-16, and I am sick and tired of them. UTF-16 is a vicious pox that plagues our software with insidious bugs forever and ever. It is clearly harmful, and should be deprecated and banned.

Other experts, such as the author of Boost.Locale, have a similar view. The key arguments follow (for many more see the links above):

  1. Most people who work with UTF-16 assume it is a fixed-width encoding (2 bytes per code point). It is not (and even if it were, like we already saw code points are not characters). This can be a source of hard to find bugs that may very well creep in to production and only occur when some Korean guy uses characters outside the Basic Multilingual Plane (BMP) to spell his name. In UTF-8 these things will pop up far sooner, as you’ll be running into multi-byte code points very quickly (e.g. Arabic).
  2. UTF-16 is not ASCII backward-compliant. UTF-8 is, since any ASCII string can be encoded the same (i.e. have the same bytes) in UTF-8 (I say can because in Unicode there may be multiple byte sequences that define the exact same grapheme clusters – I’m not actually sure if there could be different forms for the same ASCII string but disclaimers such as these are usually due when dealing with Unicode:) )
  3. UTF-16 has endianness issues. UTF-8 is endianness independent.
  4. UTF-8 favors efficiency for English letters and other ASCII characters (one byte per character). Since a lot of strings are inherently English (code, xml, etc.) this tradeoff makes sense in most scenarios.
  5. The World Wide Web is almost universally UTF-8.

So now that we know what string class we should use (std::string) and what encoding we should use with it (UTF-8), you may be wondering how we should deal with these beasts. For example – how do we count grapheme clusters?

Unfortunately, that question depends on your use case and can be extremely complex. A couple of good places to start would be UTF8-CPP and Boost.Locale. Good luck :)

Some WinDbg tips

October 30, 2014

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2014/10/some-windbg-tips/

 

I’ve gathered some WinDbg tips over time (mostly for managed dump analysis) and this seems like as good a place as any to share them, so here you go.

Preparation (one time)

  • Install the latest debugging tools from the Dev Center
    • Let’s assume you install them to c:debuggers
  • Download sosex.dll and place it in c:debuggers
  • Create an environment variable named _NT_SYMBOL_PATH with the value C:Symbols;srv*C:symbols*http://msdl.microsoft.com/download/symbols
    • Most Microsoft symbols should be found in the symbol server and will be cached in C:Symbols
    • You can copy any other symbols (PDBs) you have to the C:Symbols folder as well
  • Create an environment variable named _NT_SOURCE_PATH with the value srv* 
    • This will enable source serving (when source indexing was included in the build) – simply double-click the relevant line in the Call Stack (calls) window and you should jump straight to the relevant line in the source code.
    • You might wonder how that’s possible when no server was specified. The answer is a bit surprising – strictly speaking, there is no such thing as a source server! The name is a bit misleading. What actually happens is that the source-indexed PDB contains the proper command(s) to retrieve the relevant source files. By default this “command” would be something like C:srcfoo.cs, and it would only work if the file in the correct version is actually there. However, if source-indexing was enabled in TFS build, it would look something like tf get MyClass.cs /version:C8 (i.e. the file will be retrieved directly from source, with the correct version).

Preparation (per debugging session)

  • Open the dump file in WinDbg
    • be sure to match the architecture to the analyzed process – use WinDbg x86 for 32-bit processes and WinDbg x64 for 64-bit processes
  • Enable Debugger Markup Language (DML) by issuing .prefer_dml 1
    • This will make the output of some commands contain convenient hyperlinks
  • Load the SOSEX extension by issuing the command .load sosex.dll
    • Don’t load SOS.dll manually – the first command below (analyze -v) will load it with the correct version automatically

Debugging

  • Automatic exception analysis: !analyze -v
    • In many cases this will suffice to find the root cause!
  • Threads
    • !Threads – lists all the managed threads in the process
  • Stack commands
    • !clrstack – provides a true stack trace for managed code only
    • !dumpstack – provides a verbose stack trace
    • !eestac – runs !dumpStack on all threads in the process
    • !dso – displays any managed objects within the bounds of the current stack
    • !sosex.mk – produces and displays a merged stack trace of managed and unmanaged frames. Note that in addition to the native offset, a managed (IL) offset is specified – this is extremely useful for debugging NullReferenceException’s in that the exact offending IL instruction is indicated (actually it will be one instruction before the offending one in the specific case of NullReferenceException)
    • !sosex.mdso – dumps object references on the stack and in CPU registers in the current context
    • !sosex.mdv – displays argument and local variable information for the current frame
    • !sosex.mframe – displays or sets the current managed frame for the !mdt and !mdv commands
  • Heap commands
    • !eeheap – enumerates process memory consumed by internal CLR data structures
    • !DumpHeap – traverses the garbage collected heap
  • Object commands
    • !do – allows you to examine the fields of an object, as well as learn important properties of the object
    • !dumparray – examines elements of an array object
    • !dumpvc – examines the fields of a value class
    • !sosex.mdt – displays the fields of the specified object or type
  • Method commands
    • !dumpmt – examines a MethodTable
    • !DumpIL – prints the IL code associated with a managed method
    • !U – presents an annotated disassembly of a managed method
    • !sosex.muf – disassembles the method specified by the given MD or code address with interleaved source, IL, and assembly code
  • Exception commands
    • !pe exceptionAddress – formats fields of any object derived from System.Exception
  • GC commands
    • !GCRoot – looks for references (or roots) to an object
  • SOS Help
    • !sos.help
    • !sos.help FAQ
  • SOSEX Help
    • !sosexhelp or !sosex.help

Mismatched SOS.dll versions

!analyze -v  should get the correct SOS.dll version for you. Even if for some reason it doesn’t, SOS.dll warnings can be ignored most of the time, so long as the mscordacwks.dll version is correct (see the next section if that’s not the case). However there may be cases where the correct SOS.dll version is needed (or perhaps you just want to get rid of the warning). Here are some places to look for it (once you find it, copy it to your debugger folder and issue .load sos.dll):

  • Your best bet is the machine on which the dump was taken. Provided that it’s accessible and wasn’t patched since the time the dump was taken, the correct version should be found at C:WindowsMicrosoft.NETFramework64v4.0.30319SOS.dll. Of course it should be there on your machine as well, and your local version may happen to match (or be close enough).
  • You may find the version you’re looking for here (you should be able to extract the file from the update package itself using 7-zip).
  • You may also want to try Psscor4.dll instead of SOS.dll (Psscor is a superset of SOS) – its version need not match the dump (aside of being .NET 4.0). Note that it is less maintained than SOS.
  • For more information see http://stackoverflow.com/a/23244429/67824

Mismatched mscordacwks.dll versions

WinDBG should find the correct mscordacwks.dll automatically and download it from the symbol server. If that doesn’t happen, try and do it explicitly by running .cordll -ve -u -l (you’d might want to first run !sym noisy and/or !symfix in order to troubleshoot better – see the next section for details). Failing that, try and get the correct version from the following places, and run .cordll -u -ve -lp PathToFolderContainingMscorDAC once you have it.

  • Again, your best bet is the machine on which the dump was taken (under the same caveats as above). It should be found at C:WindowsMicrosoft.NETFramework64v4.0.30319mscordacwks.dll. As before, it will be there on your machine so you could luck out.
  • The following post lists many versions of CLR 4.0, you may be able to extract the correct version with the same method as above (use 7-zip to open the cab files inside the archive).
  • For more information see http://stackoverflow.com/a/23244429/67824

Troubleshooting missing symbols and sources

  • !sym noisy – increases symbol verbosity (always enable this when troubleshooting symbol issues)
  • .srcnoisy 3 – increases source resolution vebosity (use it when double-clicking lines in the callstack window doesn’t bring up the correct sources)
  • lm – displays the specified loaded modules
    • lme displays only modules that have a symbol problem (very useful)
  • .reload /f – forces the reloading of all symbols
    • .reload /f SomeAssembly.dll – forces the reloading of a specified DLL

For further reading try to hit F1 inside WinDbg – the documentation is very good !

Getting started with ETW using .NET’s EventSource

October 13, 2014

 

You are reading the old blog! This post has been moved to http://www.ohadsoft.com/2014/10/getting-started-with-etw-using-nets-eventsource/

 

.NET 4.5 introduced the EventSource class, allowing convenient access to Event Tracing for Windows (ETW) from managed code. This is a boon for enterprise developers, and I encourage you to go read up on it at MSDN. Also, be sure sure to check out Vance Morrison’s EventSource blog entries. Another useful blog by MS MVP Kathleen Dollard is Leaning into Windows, and Muhammad Shujaat Siddiqi’s blog is worth checking out as well.

Once you’ve got the hang of EventSource, take it to the next level with the Enterprise Library Semantic Logging Application Block (SLAB). You can do some pretty cool stuff with it, including verification of your EventSource class validity, and automatic routing of ETW events to different storage platforms (such as databases, Azure tables, and text files). Start with the developer’s guide.

Finally, if you take a close look at the documentation of the EventSource class, you’ll notice the following note:

There is a NuGet version of the EventSource class that provides more features. For more information, see Microsoft EventSource Library 1.0.16.

I couldn’t find any documentation for this mysterious NuGet package, but I did find its samples package. Once you install it you can take a look at the extensively documented code as well as debug and step into the parts you’re interested in. Be sure to read the guides that accompany the samples: _EventRegisterUsersGuide.docx and _EventSourceUsersGuide.docx – they pertain to the vanilla library that comes with .NET as well (not just the NuGet package).

One thing I couldn’t find is a list of differences between the vanilla .NET EventSource and the NuGet package. Going by the signatures, I only saw a couple more WriteEvent overloads. If you’ve read the documentation, you should know by now that that’s a good thing, however digging a little into the code I found another important difference – event type support.

The .NET version currently supports the following types (taken from ManifestBuilder.GetTypeName):

  1. Enum
  2. Boolean
  3. SByte
  4. Byte
  5. Int16
  6. UInt16
  7. Int32
  8. UInt32
  9. Int64
  10. UInt64
  11. Single
  12. Double
  13. DateTime
  14. String
  15. Guid

The NuGet package adds support for the following:

  1. Char
  2. IntPtr
  3. Byte*
  4. Byte[]

Finally, the beta pre-relase includes a very interesting overload: public void Write<T>(string eventName, T data). The data parameter is documented as follows:

The object containing the event payload data. The type T must be an anonymous type or a type with an [EventData] attribute. The public instance properties of data will be written recursively to create the fields of the event.

In other words, we should be able to use annotated custom types (in a similar fashion to WCF DataContract-annotated objects). But even better than that, we should be able to say:

[Event(1)]
public void Bar(int i, string s, DateTime d)
{
    Write(null, new {I = i, S = s, D = d});
}

Notice I said should, because I could make neither work (beta after all). But why do we even need this when we already have the WriteEvent(int eventId, params Object[]) overload? Well, let’s look at the documentation of the latter:

By default, the compiler calls this overload if the parameters for the call do not match one of the other method overloads. This overload is much slower than the other overloads, because it does the following:

  1. It allocates an array to hold the variable argument.
  2. It casts each parameter to an object (which causes allocations for primitive types).
  3. It assigns these objects to the array.
  4. It calls the function, which then determines the type of each argument so it can be serialized for ETW.

Using an anonymous types, we save the boxing (bullet 2) and unboxing (post bullet 4). I’m not sure how big of a benefit that is, so we may still be forced to use the WriteEventCore method, but I suppose every little bit helps!


Follow

Get every new post delivered to your Inbox.

Join 303 other followers