2015-12-15 48 views
1

我有樸素簡單的功能,一些HTTP狀態代碼映射到字符串:C++ 11功能的靜態變量初始化古怪

const std::string & status_name(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

有一天,我們的測試服務器上,已經發生碰撞。分析核心轉儲似乎std::out_of_range異常被拋出status_table.at(0);。另一個奇怪的是,有問題的狀態碼是200。程序崩潰濫用多線程,所以我的胃感覺告訴我,這個問題是線程相關的。

我真的很困惑它甚至有可能。它是C++ 11和這個版本的標準提供了關於靜態變量初始化的線程安全的保證。編譯器是clang-3.3,架構是x64,CPU是Intel(R)CPU E5506

的只有兩件事情浮現在腦海中:

  1. 有一個在我錯過看到代碼的一些bug;
  2. 這個怪異與CPU緩存的非一致性有關:導致崩潰的線程看到了防護變量,表示 status_table的「初始化」狀態,但是對象本身被看作是空的。

更新的問題:

做一個小的測試情況下,我能夠很容易地重現該問題後:

#include <thread> 
#include <atomic> 
#include <string> 
#include <unordered_map> 
#include <vector> 
#include <memory> 
#include <iostream> 
#include <unistd.h> 

const std::string & status_name1(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name2(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name3(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name4(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name5(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name6(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name7(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

const std::string & status_name8(int code) 
{ 
    static std::unordered_map<int, std::string> status_table { 
     { 0, "UNKNOWN" }, 
     { 100, "Continue" }, 
     { 101, "Switching Protocols" }, 
     { 200, "OK" }, 
     { 201, "Created" }, 
     { 202, "Accepted" }, 
     { 203, "Non-Authoritative Information" }, 
     { 204, "No Content" }, 
     { 205, "Reset Content" }, 
     { 206, "Partial Content" }, 
     { 300, "Multiple Choices" }, 
     { 301, "Moved Permanently" }, 
     { 302, "Found" }, 
     { 303, "See Other" }, 
     { 304, "Not Modified" }, 
     { 305, "Use Proxy" }, 
     { 307, "Temporary Redirect" }, 
     { 400, "Bad Request" }, 
     { 401, "Unauthorized" }, 
     { 402, "Payment Required" }, 
     { 403, "Forbidden" }, 
     { 404, "Not Found" }, 
     { 405, "Method Not Allowed" }, 
     { 406, "Not Acceptable" }, 
     { 407, "Proxy Authentication Required" }, 
     { 408, "Request Timeout" }, 
     { 409, "Conflict" }, 
     { 410, "Gone" }, 
     { 411, "Length Required" }, 
     { 412, "Precondition Failed" }, 
     { 413, "Request Entity Too Large" }, 
     { 414, "Request-URI Too Long" }, 
     { 415, "Unsupported Media Type" }, 
     { 416, "Requested Range Not Satisfiable" }, 
     { 417, "Expectation Failed" }, 
     { 499, "Rotten session" }, 
     { 500, "Internal Server Error" }, 
     { 501, "Not Implemented" }, 
     { 502, "Bad Gateway" }, 
     { 503, "Service Unavailable" }, 
     { 504, "Gateway Timeout" }, 
     { 505, "HTTP Version Not Supported" }, 
    }; 

    auto it = status_table.find(code); 

    if (it != status_table.end()) 
     return it->second; 

    return status_table.at(0); 
} 

int main(int argc, char ** argv) 
{ 
    std::vector<std::thread> thread_pool; 
    auto guard_variable = std::make_shared<std::atomic<int>>(0); 

    for (int i = 0; i < 8; ++i) { 
     thread_pool.push_back(std::thread([guard_variable] { 
      while (!guard_variable->load(std::memory_order_relaxed)) { 
       /// Noop. 
      } 

      int i = 0; 
      i += status_name1(200).length(); 
      i += status_name2(200).length(); 
      i += status_name3(200).length(); 
      i += status_name4(200).length(); 
      i += status_name5(200).length(); 
      i += status_name6(200).length(); 
      i += status_name7(200).length(); 
      i += status_name8(200).length(); 
      std::cout << i << std::endl; 
     })); 
    } 

    usleep(50000); 
    guard_variable->store(1, std::memory_order_relaxed); 

    for (auto && thread : thread_pool) { 
     thread.join(); 
    } 

    return 0; 
} 

此代碼按預期工作在其他地方比其他測試機器。不管是什麼版本鐺我用(3.3,3.6)這個測試程序立即崩潰,在不同的時間立即在不同的status_name*函數中。

什麼可能導致此行爲?是CPU緩存不一致還是它的錯誤libC++

+1

@JohnZwinck請參閱http://en.cppreference.com/w/cpp/container/unordered_map/at – ecatmur

+0

您應該使用'const char *'作爲映射類型而不是'std :: string'。無論如何,這是因爲你存儲的是文字。 –

+0

@JohnZwinck我完全錯過了所有觀點......我使用'std :: string',因爲我想'const std :: string&'作爲函數調用的結果,即收集'靜態'字符串。 – GreenScape

回答

1

鏗鏘支持「神奇靜態」(N2660 動態初始化和併發性破壞),因爲每個http://clang.llvm.org/cxx_status.html的值爲2.9。

而不是編譯器或您的直接代碼中的錯誤,它更有可能是代碼中其他位置的錯誤正在破壞程序狀態,導致status_table顯示爲初始化但爲空。

+0

在代碼轉儲中,它將被初始化,其中包含所有元素。當然,迭代器比較和異常傳播之間有一個窗口,但它有多大可能? – GreenScape

+0

不幸的是,更新中附帶的測試用例拋棄了這個結論。在那裏,我有一些孤立的代碼。 – GreenScape

+0

@GreenScape如果它只發生在特定的機器上,那麼這臺機器可能有一個壞的libstdC++(它提供'__cxa_guard_acquire'等) – ecatmur