2011-10-27 46 views
3

我們使用的是基本上沒有這個的雙顯式鑄造用不同的結果爲int

var t = TimeSpan.MaxValue; 
int x = (int)t.TotalMilliseconds; 

其中x最終會被傳遞給System.Threading.WaitHandle.WaitOne(INT)的API。

問題是,當這些代碼在我們的開發和分期環境中運行,它不拋出任何錯誤,但是當它在生產中運行它拋出:

Exception: System.ArgumentOutOfRangeException 
Message: Number must be either non-negative and less than or equal to Int32.MaxValue or -1. 
Parameter name: millisecondsTimeout 

當我測試這個用一個簡單的控制檯應用程序(x86和x64)x = -2147483648(int.MinValue)的結果,而當我在即時窗口中運行代碼時,我得到x = 1566804069.

發生了什麼事?

注:臨時和生產都是從單個VM克隆所以有他們

之間沒有差異THIS IS CODE,我們不能改變!否則我不會問這個問題。

+1

你轉換溢出,那爲什麼你得到兩種不同的結果,類型轉換爲長或Int64的 –

+0

@Tony沒有,如果它是* *檢查這將是一個溢出異常;所引用的例外表明*要麼*表示持續時間正在包裝,因爲它未經檢查*,或者間隔爲負開始。 –

+1

@Dustin,這不會工作;最大時間間隔是10,675,199天;以毫秒爲單位的int.MaxValue是25天。這不適合! - –

回答

4

這可以用相同的虛擬機發生的唯一方法是,如果CPU是從暫存計算機上的CPU生產機器上的不同 - 作爲Gabe在評論的問題問及zdanhis answer建議。

對於發生了什麼具體如此。對於支持SSE2的機器,.NET使用cvttsd2si指令將double轉換爲int,其中溢出映射爲0x80000000(Int.MinValue)。在沒有SSE2支持的機器上,我只能看到Rotor sources,而在jithelpers.cpp中,它只是將double轉換爲int32--它在VC10 C++上不帶SSE2,最終返回低32位的值所以傳遞給等待的值應該是1566804069(0x5D638865),正如您在即時窗口中看到的那樣。

CPU是不同的,你修改代碼的「修復」是將機器改爲不支持SSE2的機器。請參閱SSE2維基百科條目以檢查生產服務器的CPU與臨時服務器。如果你幸運的話,也許它可以在你的服務器的BIOS(或VM配置/ BIOS)中被禁用。

如果你敢,你可以嘗試修補IL來解決這個問題 - 什麼代碼真正想要的是-1作爲超時,這是「永遠等待」。通過使用ilasm和ildasm,您可能可以修復它不帶源代碼(我假設這是您無法更改它的原因)。我這樣做是成功地對自己的測試程序 - ildasm test.exe /out=test.il打開一個組裝成IL,編輯的IL最後ilasm test.il /exe創建一個新的組件。下面是我的IL看起來像什麼以及我如何修復它。

// bad code 
// var t = TimeSpan.MaxValue; 
IL_0008: call  instance float64System.TimeSpan::get_TotalMilliseconds() 

// int x = (int)t.TotalMilliseconds; 
IL_000D: conv.i4 // This is the line that becomes cvttsd2si when jitted 
IL_000E: stloc.2 

// wh.WaitOne(x); 
IL_000F: ldloc.0 
IL_0010: ldloc.2 
IL_0011: callvirt instance bool System.Threading.WaitHandle::WaitOne(int32) 

解決方法是調用wait一個

// fixed code 
// var t = TimeSpan.MaxValue; 
IL_0008: call  instance float64System.TimeSpan::get_TotalMilliseconds() 

// int x = (int)t.TotalMilliseconds; 
IL_000D: conv.i4 // This is the line that becomes cvttsd2si when jitted 
IL_000E: stloc.2 

// x = -1; // Fix by forcing x to -1 (infinite timeout) 
      ldc.i4.m1 // push a -1 
      stloc.2 // pop and store it in 'x' 

// wh.WaitOne(x); 
IL_000F: ldloc.0 
IL_0010: ldloc.2 
IL_0011: callvirt instance bool System.Threading.WaitHandle::WaitOne(int32) 

注意,在這種情況下, 'x' 爲本地#2之前重新加載X(此處位置2)-1 - 在IL在該方法的頂部會給你正確的#所以需要改爲無論#x被已經被分配在stloc.2 2,這應該只是前調用了WaitOne在標籤IL_0010在匹配在ldloc指令#我例。

+0

我給你這個努力的答案。我結束了1)報告的錯誤,他們ar自我修復它,並讓我知道什麼時候一個新版本2)我使用PostSharp應用截取Milliseconds屬性的一個方面,我將它改爲適當的值(花式的方式做你的建議)。但是你提供了一個很好的解決方案。 –

0

您的轉換溢出,爲什麼你得到不同的系統不同的結果。 使用長而不是

var t = TimeSpan.MaxValue; 
    long x = (long)t.TotalMilliseconds; 
+0

請REREAD的問題,這是在我們正在消費的API,所以我們不能改變它,否則我不會問這個問題。 –

+1

沒有,*正確*要做的只是調用'了WaitOne(-1)',但OP不能這樣做,因爲他是唯一的環境中,而不是代碼的控制。 – Gabe

0

TimeSpan.MaxValue是equivelant到Int64.MaxValue這是太大的傳遞給了WaitOne()值。如果你想傳遞一個大的值,只需使用Int32.MaxValue。

+0

這不是我無法更改的代碼,您的回答無效。 –

+1

我不明白,你有代碼崩潰,但你不能改變它?你期望如何解決這個問題? –

+0

我可以看到使用ILSpy的代碼。 http://wiki.sharpdevelop.net/ILSpy。我不希望修復代碼,我想知道爲什麼它可以在兩個系統上工作,但不是另一個。 –

4

TimeSpan.MaxValue.TotalMilliseconds是等於922337203685477的double,它大於Int32.MaxValue(2147483647)。在這種情況下,演員會做的是特定實現(技術上它是undefined請參閱下面的@ phoog評論),並且可能取決於CPU,這可能會解釋您所看到的差異。

在一種情況下,演員陣容會導致System.Threading.WaitHandle.WaitOne(int)可接受的值,而另一種情況則不然。

這似乎是您正在使用的庫中的一個錯誤。有一個WaitOne超載需要TimeSpan作爲參數,所以我不知道他們爲什麼不使用它。如果你不能改變圖書館,你是不走運的。

+0

是的,這是一個錯誤。非常刺激。 –

+0

你確定它是實現特定的嗎?其他操作(附加,乘法等)的行爲在溢出條件中已完全指定(選中vs未選中) - 我覺得縮小轉換不會被限定。 –

+0

@Marc Gravell ECMA 335,分區III,3.27 conv。 - 數據轉換「如果發生溢出將浮點類型轉換爲整數,或者如果將浮點值轉換爲整數是NaN,則返回的值未指定。 – phoog