Disclaimer: this is an automatic aggregator which pulls feeds and comments from many blogs of contributors that have contributed to the Mono project. The contents of these blog entries do not necessarily reflect Novell's position.
Two revolutionary phones coming out at the same time and a third that people will think is revolutionary (just kidding).
My thoughts coming from my thoughts a developer and what I know from my experience as a OSS developer and enlightened consumer myself. I’ve done lots of mobile development in the past. Limited by J2ME or Brew, horrible systems that limit my break in to the market put in my cell providers, having to get approval to run the software on my own phones or customers phones from the manufacture and the cell company using signatures and keys (killing open source efforts for the phones). It’s a nasty game.
OpenMoko
Very interesting. I defiantly will be ordering the OpenMoko Freerunner as soon as it comes out to evaluate after it comes out. I’ve been following the project and tinkering with the developer kit in the past. It’s Linux, I can do what I want, its not sold by the cell company, and so it’s mine to do what I want and it’s free for me to change it. This appeals to me as a developer and is great for customers because it brings variety and customization, cheaper software and better quality.
The Freerunners hardware could be better, but they are trying to use components with only hardware with publicly documented interfaces or using open standards or provide free drivers, so it’s hard. Only USB 1.1 USB, no camera, a limited to 256mb of internal flash hurt (with SD card support we may be able to get up to 8gb). The hardware is open as well as OpenMoko publishes the CAD designs for everything, and its packed full of interfaces to stick add on new things.
iPhone 2.0 and 3G
I was one of the first to stand in line to get the iPhone. I was one of the first to run the activation hacks and jailbreak it. I seen the potential of the platform for myself. When apple anonocced the developer program I was extreemly happen until I read all the restrictions. Feels like everything I faced before. I’ve signed up and have been rejected by the developer program, so no chance of my application running on through app store (thankfully the jailbreaking community wasn’t deterred by this program so I will continue my development plans.
I will still likely upgrade my iPhone to the 3G version though. One thing is that the iPhone chick magnet of hotness ( just kidding
).
Andriod
Its neat, but I don’t think it will get as accepted highly by the development community. It’s going to be hard to convince people to rewrite their applications since Adriod’s system pretty requires it to run since everything must run on their VM. Sure its linux and its going to open, but its like BSD open and the people selling the phones are likely to lock it down for there versions preventing modification, so no native code will not be able to be introduced. Just not doing it for me.
Today I walked into Aaron's office unannounced and I just saw him glowing. Like a girl that has been kissed for the first time, like a donkey in the spring.
A voice in the background was narrating Banshee features, and I was wondering just what is that noise?
As I went around to his monitor to see what he was watching and listening to, I saw this Linux.com review on Banshee that included a screencast/podcast.
He was *so* excited that he was actually watching it in three computers at once.
I could not believe it.
Three computers at once. One, two and three. All playing the podcast. At the same time.
I was speechless.
From economic mastermind to flattered developer.
He said to me: "I have never seen a production of such caliber" as he listened to the background music in the above podcast.
I just stood there quietly. Unsuspectingly recovering the Twix office supply.
Yesterday I forgot to point to the actual page to install the Moonlight plugins.
You can download the latest plugin from here. Just like the last release, these plugins are compiled without ffmpeg support.
The source code is available here.
You can track the progress and try out a few applications yourself from our Moonlight Status page.
A new release of Moonlight is now available. The team has been working very hard on improving the performance of Moonlight as well as improving our compatibility with Microsoft's Silverlight.
Get your copy here. Source code is moon-0.7.tar.bz2.
This release will also work with both Firefox 2.0 and Firefox 3.0. We have also switched our installation system to use signed XPIs, but we will also require a browser restart (we could not figure out a way of avoiding this).
Some of my favorite work that happened on this cycle is the effort to improve our multi-browser support, work towards supporting WebKit and Opera is underway and will improve over time. This work benefitted from our own work to support both Firefox 2.0 and 3.0 in the plugin.
Windowless mode (the mode that allows blending of HTML content and Silverlight content) is vastly improved but is only available on Firefox 3.0. This is a feature that is used extensively by Silverlight designers.
More details from the release:
We have just released Moonlight 0.7 to the public.
Get your copy here.
This new version of Moonlight works on both Firefox 2.0 and 3.0 and sports some significant changes from 0.6:




Last time we saw that CLR exception handling is significantly slower than HotSpot exception handling. This time we'll look at two very variations of the ExceptionPerf1 microbenchmark that significantly affect performance.
I've highlighted the changes.
Variation 1
public class ExceptionPerf2 {
public static void main(String[]
args) {
long start = System.currentTimeMillis();
for (int i
= 0; i < 100000; i++) {
try {
Integer.parseInt("");
}
catch (NumberFormatException x)
{
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
Variation 2
public class ExceptionPerf3 {
static NumberFormatException exc;
public static void main(String[]
args) {
long start = System.currentTimeMillis();
for (int i
= 0; i < 100000; i++) {
try {
Integer.parseInt(null);
}
catch (NumberFormatException x)
{
exc
= x;
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
Results:
| HotSpot 1.6 x86 | .NET 1.1 SP1 | .NET 2.0 SP1 x86 | Mono 1.9 x86 | |
| ExceptionPerf1 | 111 | 14743 | 3590 | 537 |
| ExceptionPerf2 | 140 | 15735 | 10761 | 36309 |
| ExceptionPerf3 | 112 | 14946 | 9728 | 24107 |
.NET/Mono results with IKVM 0.36
Why do these small changes have such a big perf impact?
Both these changes result in additional stack trace data being collected. IKVM has
some optimizations that prevent gathering stack traces in very specific circumstances.
Normally when you create a Java exception object, the Throwable constructor
will call Throwable.fillInStackTrace().
However, since this is a very expensive operation, IKVM tries to remove this call
when it is unnecessary (i.e. when it sees that you immediately throw the exception
and the exception type doesn't override Throwable.fillInStackTrace()).
Additionally, in Java an exception object will always have the complete stack trace,
but a .NET exception only has the stack frames from the throw to the catch site. This
means that at the catch site IKVM will collect the rest of the stack trace, unless
the exception object isn't used (as in the ExceptionPerf1 microbenchmark).
The time it takes to collect a stack traces obviously depends on the call stack depth, so let's look at a microbenchmark to measure that effect:
class ExceptionPerf4 {
public static void main(String[]
args) {
nest(Integer.parseInt(args[0]));
}
static void nest(int depth)
{
if (depth > 0) {
nest(depth - 1);
} else {
run();
}
}
static void run() {
Exception x = new Exception();
long start = System.currentTimeMillis();
for (int i
= 0; i < 100000; i++) {
x.fillInStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
Results:
| Depth | HotSpot 1.6 x86 | .NET 1.1 SP1 | .NET 2.0 SP1 x86 | Mono 1.9 x86 |
| 1 | 64 | 2930 | 4611 | 19377 |
| 10 | 85 | 3814 | 6787 | 34895 |
| 100 | 380 | 12500 | 27935 | |
| 1000 | 3543 |
For the curious, the IKVM implementation of Throwable.fillInStackTrace() is
essentially new System.Diagnostics.StackTrace(true);
Next time we'll wrap things up.
at 6/28/2008 8:10:41 PM poupou said... Added ReplaceIncompleteOddnessCheckRule to Gendarme. This will detect bad checks for oddness, like ((x % 2) == 1), which wont work for negative numbers. The fixed version would be ((x % 2) != 0) but an even better one, avoiding the modulo, is: ((x & 1) == 1). |
at 6/28/2008 2:13:09 PM poupou said... with jbevain benediction (pun intended ;-) I started applying Gendarme performance rules into Cecil. |
at 6/27/2008 10:23:26 PM poupou said... Earlier today Zoltan, aka vargaz, made a nice commit to change a few Type.GetType(string), which requires reflection, into faster typeof(x). |
at 6/27/2008 8:45:45 PM poupou said... Lot of gray today, but no rain yet!!! (and I'm doing my best to ignore this weekend weather reports) |
at 6/25/2008 10:13:49 PM poupou said... It *almost* did not rain today, except after diner :| since I don't like watching rain... |
at 6/23/2008 8:24:28 PM poupou said... I just added DoNotRoundIntegersRule to Gendarme and a few instruction rocks to go along. Good news: no defect was found inside Mono class libraries. Bad news: it rains everyday |
at 6/16/2008 9:21:46 PM poupou said... Still rainy :-( Anyway I committed ReviewUselessControlFlowRule for Gendarme. This is another rule to check for typos (like an extra ';' after a condition) and a great way to find empty code blocks (like forgotten TODO ;-). There are a bit over 100 of them inside Mono right now... |
It's been a while since I blogged here (again) but I got some good excuses... like:
Right now I'm using the journal to to describe all new things, mostly rules, being added into Gendarme. It works well (it's fast) but does not reach as many people as Monologue. My original idea was to post, from time to time, summaries in my own blog. I failed that - I guess I'm not so good at duplicating content ;-)
Since I'm (hopefully) better at coding (than either blogging or duplicating) I created a small application (to learn a bit about XLinq) that reads the Gendarme Journal and create a "weekly news" RSS entry. Look for it soon on monologue.
As a software developer I've worked with a lot of APIs and developer tools over the years and had many good experiences and a few bad ones. The bad ones are where you waste a lot of time trying to get something to work and it just doesn't do what its supposed to. These kinds of things make you angry and make you resent the vendor because they make you look bad as a developer and they cost you a lot of time, and time is money. The worst cases are those where you have no real choice, you have to use the vendors products or APIs. The latest bad experience I've had is with PayPal. Their developer sandbox just doesn't work and its been eating up my time trying to make it work. I have followed the API documentation closely and am 100% sure I'm doing the right things in my code but it doesn't work. Yet, how can I ignore PayPal if I want to implement ecommerce? I can't because they are the most popular provider. I simply have to get it working. Maybe I will have to test on their production site and then issue refunds. This is what some others have resorted to if you read their forums. You end up paying the transaction fees though even if you do issue refunds. Its a wonder to me that PayPal is so dominant given these shortcomings. I've implemented google checkout and Authorize.NET and they both worked as expected using their sandboxes.
I've created a Camtasia movie here showing the problems, but to summarize:
PayPal offers 2 products, PayPal Express and PayPal Pro/ Direct Pay. The Direct Pay allows you to charge the customer right from your own site by letting them enter their credit card info, this costs you $30/month to enable the service. Express checkout doesn't have this monthly fee but requires the user to pay at the paypal site with their paypal account.
Using the NVP (Name Value Pair) API to process Express checkout, the process is.
1. Make a call to the PayPal NVP web service using the SetExpressCheckout call. You recieve back a paypaltoken and then you redirect the user to paypal passing this same token. This call works as expected, you get the token and you redirect.
2. After the customer pays at PayPal, PayPal redirects them back to your designated page and passes the paypaltoken again. Its the same token as the one returned from the previous call to SetExpressCheckout. Next you are supposed to call GetExpressCheckoutDetails passing the same token back to PayPal. This call fails with the error "Security header is not valid". When you look this up or google it, its supposed to mean that you did not pass the correct API credentials, but believe me, I'm passing the right credentials and its the same credentials that worked fine in the call to SetExpressCheckout. If the call to GetExpressCheckoutDetails worked as its supposed to, the next step would be to call DoExpressCheckoutPayment which is where the order would be completed.
To use the DirectPay API you need to accept the billing agreement which would cost you $30/month on production but should be free on the sandbox. However, when you click the I Agree button in the sandbox account it doesn't work so you can't get your sandboz account enabled to use the DirectPay API.
So, in short, the PayPal sandbox just doesn't work. You can't reliably test the Expess Checkout or the DirectPay API. You would think the so called industry leader in payment processing could do a better job with this. PayPal are you listening? Please please please fix this crap and stop making me waste my time. Are you really going to make me use the production site for testing? Is that some angle to help you squeeze me for $30/month or are you just incompetant?
UPDATE: In case you think I'm being too hard on PayPal, I captured another little video to show how difficult it is to file a support ticket. I have not figured out how to do it yet. I had this same problem yesterday which is why I resorted to blogging in hopes of getting some attention from PayPal to address the sandbox problems.
Back in October of 2006 when I first launched my company, Source Tree Solutions to work full time on mojoPortal, I had kind of a fuzzy business plan with the main idea being that I would make revenue by offering consulting around the free mojoPortal product. I guess in my wide eyed optimism I thought that doing this kind of consutling would improve mojoPortal and in fact it did to some extent. A few customers actually sponsored development for things that improved mojoPortal for everyone. But most of the consutling was really for things that were not of any general benefit beyond the customer's needs, so in some cases I felt like taking on this work was actually slowing me down from progress on improving mojoPortal. Forming a business around consulting means you always have to be taking on more projects to keep the business going because you don't make any money unless you are doing billable work. Now I am in the process of shifting my strategy to sell some premium features on top of the free mojoPortal core product. I think that selling some products in order to generate revenue will be better because it will pay for the free time I need to keep improving the mojoPortal. Consulting will remain a part of my business model but my plan is to keep it very limited goig forward.
So with this changing strategy in mind I've been working feverishly to get my first product completed so that I can open up a Store here on mojoPortal.com in early July and begin selling it. The first feature for sale will be a more advanced Event Calendar that allows selling tickets to events. Following that I plan to build a Form Wizard that allows users to easily create forms to capture arbitrary data. I also plan a Fund Raiser feature and an add on package for Web Farm support. I have a lot of ideas for premium features, but my main goal is to just get enough revenue rolling in to allow me to keep improving the core. There are a lot of planned improvements for the core that I'm eager to complete, like built in support for tagging, comments, and content versioning so that they can easily be enabled for any feature with little effort. Eventually I'd like to open the store up so that other developers can also sell products there too, so that it can be a market place something like snowcovered.com which sells add ons for DNN.
One of the reasons I chose to implement a commerce enabled feature as my first product was so that I could figure out which pieces of the commerce architecture need to b re-usable. We already have a WebStore project that I will be using to sell my premium products and this WebStore is one of the free open source features of mojoPortal. So far the WebStore is very rudimentary, it can only sell download products and its really a bare bones implementation for the product catalog at the moment. The project was originally started under a sponsorship by BrainBeacon but was never completed fully because they went out of business before ever opening a store. Its hard to really polish up a complex feature like ecommerce unless you are actually using it for business or supporting a customer who is using it. So using it to sell my own products will lead to a lot of improvement in the WebStore feature. I've been doing a lot of re-factoring in the WebStore to get the re-usable pieces built into mojoPortal core so they can be used across features. For example, Country List, State List, Currency List, Language List and their administrative features were originally in the WebStore projects but I've moved them into the core of mojoPortal because they will be needed by other features. Support for Payment gateways like Google Checkout and PayPal are also being moved into the core so they can be re-used by any feature that wants to implement ecommerce. Since my first product will be an Event Calendar that allows selling tickets it will need to leverage a lot of the same ecommerce code that the WebStore does. Rather than re-implement it in every feature it has forced me to think about the best way to make most of it easily re-usable.
Implementing this more advanced Event Calendar has also led me to other improvements in the core of mojoPortal. For example one of the features in my new Event Calendar is support for recurring events. When you create a recurring event it actually creates event instances going out x number of years. Since events are also searchable using the site search, you have to update the search index for each created event as well. What I found was that creating events in rapid succession due to recurrence could lead to some problems due to the way I was handling the indexing of items. The indexing was triggered by the saving of the event then code to update the index was spawned on a new thread so that it doesn't block the UI. Under some circumstances the writing to the index was not in the proper sequence and errors could occur. So I implemented a queue in the database so that items to be indexed could be queued and then processed in batch on a background thread while keeping the sequence correct because everything is processed in queue sequence.
Another thing that got improved in the core as I implemented this new Event Calendar is module settings, which is just a place to store feature specific settings for instances of a feature. In a number of the mojoPortal features we have support for google maps, but some of the settings needed for google maps were not well supported by module settings. For example the google maps api defines some constants for the Map Type like G_NORMAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP, etc. When I first enabled support for specifying the map type, it was done in module settngs by entering the constant in a textbox like this:

pretty yucky and not user friendly since its easy to put something incorrect there and nothing to tell the user what is acceptable. So as much as this bothered me I could not conceive of shipping a paid product with this kind of setting so I implemented ISettingControl as a way to introduce a possibility to use a custom UserControl for this setting and now for all features that use the google map the setting looks like this:

much better implemented as a dropdown list! Actually not shown in the screen shot is I also implemented a dropdown for the zoom level so that it is limited to the range of acceptable values.
So, in short, developing features to sell has made me think more deeply about what is needed in the core of mojoPortal to support 3rd party feature development more easily, because its made me think more like a third party.
I haven't blogged much this month because I've been so busy working to get this feature completed and to get the store opened, but thught I should go ahead and post this to let people know what I'm doing. Several people have asked recently what happened to the store demo site as it is currently off line. I will have that site back up soon. I just have a little more re-factoring of the WebStore code to do and I will setup the demo store again.
I'm very excited about the new store opening soon and will announce it here in the blog as soon as its open. There will also be anew release of mojoPortal soon with the improvements I've mentioned above as well as a few bug fixes and skin tweaks to better support Firefox 3.
In my last post, I said I'd write the sample in C# to compare to F#. Well, I grossly underestimated the power of query comprehensions. The C# version is almost the same length (formatting differences, mainly). I'm surprised and impressed. (Or maybe I'm writing F# like I'd write C#.) Edit: I think maybe sequence expressions could cut it down a bit...
...But... C# still can't do discriminated unions efficiently or effectively ;).
1 // crudcreatecompare.cs: Generates LINQ CRUD table fields using the horribly named DatabaseBase code
2 //
3 // Tables look like: [Table(Name="dbo.Accounts")]
4 // Columns look like this:
5 // [Column(Storage="_AccountName", DbType="VarChar(128) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
6 // [DataMember(Order=1)] // Exists if serialization is turned on; used to order key parameters
7 // Emits:
8 // public static readonly TableHelper<Account, String> Accounts =
9 // CreateTable(dc => dc.Accounts, a => a.AccountName);
10
11 using System;
12 using System.Collections.Generic;
13 using System.Data.Linq.Mapping;
14 using System.IO;
15 using System.Linq;
16 using System.Reflection;
17 using System.Runtime.Serialization;
18
19 class Program
20 {
21 static void Main()
22 {
23 Console.WriteLine(
24 new Program().generate(
25 "C:\\yourlinq.dll"));
26 }
27
28 string generate(string asmpath)
29 {
30 var asm = Assembly.LoadFrom(asmpath);
31 var lines = from t in asm.GetExportedTypes()
32 let ta = getAttr<TableAttribute>(t)
33 where ta != null
34 let name = pluralize(ta.Name.Replace("dbo.", ""))
35 orderby name
36 select genTable(t, name);
37 return joinStrings("", lines);
38 }
39
40 string genTable(Type t, string tableName)
41 {
42 var keyProps =
43 from p in t.GetProperties()
44 let c = getAttr<ColumnAttribute>(p)
45 where c != null && c.IsPrimaryKey
46 let dm = getAttr<DataMemberAttribute>(p)
47 orderby dm == null ? 0 : dm.Order
48 select p;
49 var tw = new StringWriter();
50 tw.WriteLine("public static readonly TableHelper<{0}, {1}> {2} =",
51 t.Name,
52 joinStrings(", ", keyProps.Select(p => p.PropertyType.Name)),
53 tableName);
54 tw.WriteLine("\tCreateTable(dc => dc.{0}, {1});",
55 tableName,
56 joinStrings(", ", keyProps.Select(p => "a => a." + p.Name)));
57 tw.WriteLine();
58 return tw.ToString();
59 }
60
61 string joinStrings(string sep, IEnumerable<string> items)
62 {
63 return string.Join(sep, items.ToArray());
64 }
65 string pluralize(string s)
66 {
67 return s.EndsWith("s") ? s : s + "s";
68 }
69
70 T getAttr<T>(ICustomAttributeProvider icap)
71 {
72 var a = icap.GetCustomAttributes(typeof(T), true);
73 return a.Length == 0 ? default(T) : (T)a[0];
74 }
75 }
With the DatabaseBase and TableHelper classes, you still have to generate a CreateTable field per table. Why do it by hand? I wrote an F# script to generate the statements. I must say, I'm loving F# more than I had hoped. The more I learn, the better it gets. I've noticed (even in C#) that using a functional style generally means less errors. This script worked without bugs the first time (i.e., as soon as it compiled), which is pretty cool (granted, it's not big, but I'm sure if I had tons of for loops, I woulda messed up somewhere). Maybe this weekend I'll try writing it in C# just to compare (pretty sure it'll be more than 59 lines!). And I'll preempt comments about readability: Yes, it may be difficult to read if you don't know F#, but more on that later...
I'd love feedback as to making it more "functional"; years of imperative programming don't die quickly. Also, I'm not very happy with the definition of chooseAttr, but I can't seem to get it to infer the type I want otherwise.
Anyways, here's the code. You'll need to specify the references when compiling: -r "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.Linq.dll" -r "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Runtime.Serialization.dll"
1 // crudcreatetable.fsx: Generates LINQ CRUD table fields using the horribly named DatabaseBase code
2 //
3 // Tables look like: [Table(Name="dbo.Accounts")]
4 // Columns look like this:
5 // [Column(Storage="_AccountName", DbType="VarChar(128) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
6 // [DataMember(Order=1)] // Exists if serialization is turned on; used to order key parameters
7 // Emits:
8 // public static readonly TableHelper<Account, String> Accounts =
9 // CreateTable(dc => dc.Accounts, a => a.AccountName);
10
11 #light
12 open System
13 open System.Reflection
14 open System.Data.Linq.Mapping
15 open System.Runtime.Serialization
16
17 let getAttr<'target, 'a when 'a :> ICustomAttributeProvider> (ty : 'a) =
18 match List.of_array (ty.GetCustomAttributes(typeof<'target>, true)) with
19 | a::_ -> Some (a :?> 'target)
20 | [] -> None
21 let chooseAttr<'target, 'a when 'a :> ICustomAttributeProvider> (ty : 'a) =
22 match getAttr<'target,_> ty with
23 | Some(a) -> Some(ty,a)
24 | None -> None
25
26 let joinStrings (sep:string) items = items |> Seq.fold1 (fun acc x -> acc + sep + x)
27 let pluralize (name:string) = if name.EndsWith("s") then name else name + "s"
28
29 let generate(asmpath:string) =
30 let genTable (t:Type, tableName) =
31 let keyProps =
32 t.GetProperties()
33 |> Seq.choose (chooseAttr<ColumnAttribute,_>)
34 |> Seq.filter(fun (p,c) -> c.IsPrimaryKey)
35 |> Seq.map(fun (p,_) -> p, getAttr<DataMemberAttribute, _> p)
36 |> Seq.orderBy(function | _,Some(dm) -> dm.Order | _ -> 0)
37 |> Seq.map (fun (p,_) -> p)
38 let tw = new IO.StringWriter()
39 let pn fmt = Printf.twprintfn tw fmt
40 pn "public static readonly TableHelper<%s, %s> %s ="
41 t.Name
42 (joinStrings ", " (keyProps |> Seq.map (fun p -> p.PropertyType.Name)))
43 tableName
44 pn "\tCreateTable(dc => dc.%s, %s);"
45 tableName
46 (joinStrings ", " (keyProps |> Seq.map (fun p -> "a => a." + p.Name)))
47 pn ""
48 tw.ToString()
49
50 let asm = Assembly.LoadFrom asmpath // Don't use ReflectionOnly 'cause it won't resolve dependencies
51 asm.GetExportedTypes ()
52 |> Seq.choose (chooseAttr<TableAttribute,_>)
53 |> Seq.map (fun (t,ta) -> t, ta.Name.Replace("dbo.", "") |> pluralize)
54 |> Seq.orderBy (fun (t,_) -> t.Name)
55 |> Seq.map genTable
56 |> Seq.fold1 (+)
57
58 generate "C:\\yourlinq.dll"
59 |> Console.WriteLine
[Reposting because it appears to have been deleted somehow.]
A bit ago, I posted some info on doing CRUD operations using LINQ: http://www.atrevido.net/blog/2007/08/26/A+LINQ+To+The+CRUD.aspx
This is a lightweight way (no codegen at all, only 2 lines of code per table) to get some CRUD stuff with LINQ. It's not the most efficient or fantastic way of doing things, but it works fine in the several projects we've used it so far. And we get to use C# 3's expression trees, which is a fantastic and under-exploited feature. At any rate, it does show that doing disconnected work with LINQ is trivial.
The code I posted was for Beta 2 and no longer works. I've since added a few new features, but the basic idea remains the same as before. I'm just posting the new code as I've gotten a few emails and one comment about the old code no longer working.
While working on it in a real project, I started using our Tuple struct, so you'll
need that too:
Tuple.cs (2.8 KB)
As Scott Peterson pointed out, this class doesn't implement IEquatable<T>. Just add it and call the == operator.
As always I welcome any criticism.
#!/usr/bin/env python
# Sets Pidgin status from XChat nick, assuming you use a style like:
# mynick => Available (status "Workin")
# mynick|busy => Away (status "busy")
# Hobbled together by Sandy Armstrong from code
# written by Ryan Paul (segphault) of Ars Technica:
# http://arstechnica.com/reviews/apps/pidgin-2-0.ars/4
# http://arstechnica.com/journals/linux.ars/2007/08/29/send-twitter-updates-from-xchat-using-python
import xchat, dbus
# Describe the plug-in metadata for XChat
__module_name__ = "Pidgin Status Plug-in"
__module_version__ = "1.0"
__module_description__ = "Set Pidgin status according to XChat nick"
# Specify status ID values
STATUS_AVAILABLE = 2
STATUS_AWAY = 5
def nick(words, word_eol, userdata):
# Get new nick
if len(words) < 2:
return None
new_nick = words[1]
# Check for status
pipe_index = new_nick.find("|")
if pipe_index > 0 and pipe_index < len(new_nick):
message = new_nick[pipe_index + 1:]
# set away with status message
set_status("", STATUS_AWAY, message)
else:
# set available
set_status("", STATUS_AVAILABLE, "Workin")
def set_status(name, kind, message):
# Create a new saved status with the specified name and kind
status = purple.PurpleSavedstatusNew(name, kind)
# Associate the specified availability message with the new saved status
purple.PurpleSavedstatusSetMessage(status, message)
# Activate the new saved status
purple.PurpleSavedstatusActivate(status)
# Initiate a connection to the Session Bus
bus = dbus.SessionBus()
# Associate Pidgin's D-Bus interface with Python objects
obj = bus.get_object(
"im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
# Associate the nick function with the /nick command in XChat
xchat.hook_command("nick", nick)
I’m always amazed by the generosity of my friends and family at this time of year. With only a week and a half, I not only raised the minimum for participation in this year’s ride but had to change my goal twice! [...]
I upgraded the Linux OS on my laptop yesterday so that I had a more up-to-date GNOME installation such that I could actually build Evolution from SVN and hack it a bit to solve some annoyances I've been having. However, this is for another blog post.
So... PulseAudio. W. T. F.
All day yesterday I'd been wondering why my sound volume was so low even though I had cranked it all the way up to 100%. Since I was mostly focused getting work done yesterday, I didn't spend any time investigating until last night when I was forced to do so.
I don't remember the exact order of things, but I did watch an episode of Harsh Realm in Xine while also IM'ing some friends in Pidgin, but the sound was so low I could barely hear anything. So a friend on IRC suggested I kill pulseaudio and restart it as this supposedly would help fix the "wonkyness" (evidently I'm not the only one with PulseAudio problems).
This worked for a while, long enough to play the DVD iirc, but afterward I had no sound. So I thought "well, I guess this is just more PulseAudio 'wonkyness', so let me kill pulseaudio again". This time when I killed it, my sound card got flooded with every audio event that had happened over the hour or so I was running Xine. Yay. (that was a sarcastic yay in case you couldn't tell)
Audio worked again for a bit. At one point, a friend IM's me a link so I clicked it and no browser started up (which seemed a bit odd). Tried it again, still nothing. So I figured "ok, I'll just launch firefox from my gnome-terminal and see if that prints any errors". No errors, but it did just hang. Oh goodie. Ctrl-C'd and killed pulseaudio again. At this point my friend IM'd me again which made Pidgin completely lock up:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
26422 fejj 20 0 3054m 1.3g 27m D 198 33.2 18:55.68 pidgin
So the issues of memory usage are perfect for another rant blog post, but the point is that pidgin is completely locked. I can't even kill -9 it (I tried multiple times).
I had to Ctrl+Alt+F1, login as root, and `init 3`
At this point I was so frustrated that I uninstalled each and every package with pulseaudio in the name before going back to X.
Low and behold, after that my audio works flawlessly (well, the Pidgin IM audio events make some crackling which I don't recall happening before I reinstalled, but I can live with it - especially since other audio seems crisp).
So yea... the stuff that the Linux Hater says is all true. He even complained about audio under Linux a while back (couldn't remember which entry it was with a quick look over the entry names).
The point of this story is that PulseAudio appears to be a solution in search of a problem (quite common in the land of Linux afaict).
As I was bitching about this to a hacker friend of mine, he asked me "isn't PulseAudio for sound mixing or something? So multiple programs can all play audio at the same time?".
Nope, because ALSA seemed to do mixing just fine for me in the past.
A quick Google search for PulseAudio reveals that it is supposed to allow fancy stuff like redirecting sound to another host machine for playing.
...just as I suspected: a solution in search of a problem.
Seriously though, who the hell needs that feature on their desktop/laptop machines? No one, that's who. The only people who might need that are thin clients - so why install it by default? Why not just have the sysadmins for those thin-client networks set this up?
PulseAudio is a clusterfuck for basic desktop audio needs afaict.
Sigh.
Update: Apparently I offended the PulseAudio developer with my "it's a solution in search of a problem" statement. For that, I apologize. A number of people have explained what exactly it is supposed to be solving. Unfortunately, none of the problems it is solving seem to be problems I need solved (maybe my sound cards in all my machines have hardware mixing support? I don't know. Or maybe dmix is plenty good enough for me).
I also apologize because apparently some (not all) of the problems I was experiencing were because of brokenness in my distro (or so it is suspected). For that, I also apologize.
However, I still get the feeling that PA is not quite ready for wide desktop adoption because people on other distros are having some of the same problems I had and this is not good.
I'd expect a similar rant from people if Evolution's current IMAP provider was replaced with my IMAP4 provider. As nice as my IMAP4 provider replacement is for my usage, there are no doubt new problems that it might currently have over the old implementation that would break things for some people. (Hence why I have not pushed for it to replace the old IMAP provider until I'm sure it is ready.)
Also, this was tagged as a rant. I had every right to be frustrated because things weren't working like they have worked Just Fine(tm) for me for years. I've had to put up with far worse personal attacks on myself and software I've been involved with over the years than anything I said on this blog (and lets face it, this was not a personal attack aimed at the PA devs - it was just me venting my frustration with a piece of software), so I think some people took this post way too personally.
One area where IKVM performance is much worse than HotSpot is in throwing and catching exceptions. This blog is the first of three that will look into why this is the case.
We start out with two microbenchmarks to highlight the differences.
Java
public class ExceptionPerf1 {
public static void main(String[]
args) {
long start = System.currentTimeMillis();
for (int i
= 0; i < 100000; i++) {
try {
Integer.parseInt(null);
}
catch (NumberFormatException x)
{
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
C#
using System;
class ExceptionPerf1 {
public static void Main(string[]
args) {
int start = Environment.TickCount;
for (int i
= 0; i < 100000; i++) {
try {
throw new Exception();
}
catch (Exception)
{
}
}
int end = Environment.TickCount;
Console.WriteLine(end - start);
}
}
Results:
| HotSpot 1.6 x86 | .NET 1.1 SP1 | .NET 2.0 SP1 x86 | Mono 1.9 x86 | |
| Java* | 111 | 14743 | 3590 | 537 |
| C# | 11139 | 2605 | 187 |
*.NET/Mono results with IKVM 0.36
This shows that the situation on the CLR is pretty bad. If you care about exception throwing performance, please complain to Microsoft instead of to me. Although I expect that they'll tell you not to throw so many exceptions. ;-)
On the next episode we'll see that the above microbenchmark is actually a best case scenario for IKVM and we'll see how things can get much worse...
Andy Malakov wrote an Ant task for ikvmc. It also contains a doclet that can generate an xml mapping file that contains all the parameter names that ikvmc can then attach to the .NET methods (ikvmc already does this for methods with debugging information, but that doesn't work for abstract methods).
Good stuff!
