[{"data":1,"prerenderedAt":806},["ShallowReactive",2],{"post-lock-free-stack":3,"home":784},{"id":4,"title":5,"body":6,"date":773,"description":774,"extension":775,"meta":776,"navigation":109,"path":777,"seo":778,"stem":779,"tags":780,"__hash__":783},"blog\u002Fblog\u002Flock-free-stack.md","无锁的线程安全栈",{"type":7,"value":8,"toc":764},"minimark",[9,16,20,140,153,157,160,244,255,258,261,271,276,279,313,320,323,340,408,411,420,752,755,760],[10,11,12],"blockquote",{},[13,14,15],"p",{},"选自《C++ Concurrency In Action》7.2.4",[17,18,19],"h2",{"id":19},"数据结构",[21,22,27],"pre",{"className":23,"code":24,"language":25,"meta":26,"style":26},"language-cpp shiki shiki-themes github-light github-dark","template \u003Ctypename T>\nclass LockFreeStack {\n private:\n  struct Node;\n  struct CountNodePtr {\n    int external_count;\n    Node* ptr;\n  };\n  struct Node {\n    std::shared_ptr\u003CT> data;\n    std::atomic\u003Cint> internal_count;\n    CountNodePtr next;\n\n    Node(const T& value)\n        : data(std::make_shared\u003CT>(value)), internal_count(0) {}\n  };\n  std::atomic\u003CCountNodePtr> head;\n};\n","cpp","",[28,29,30,38,44,50,56,62,68,74,80,86,92,98,104,111,117,123,128,134],"code",{"__ignoreMap":26},[31,32,35],"span",{"class":33,"line":34},"line",1,[31,36,37],{},"template \u003Ctypename T>\n",[31,39,41],{"class":33,"line":40},2,[31,42,43],{},"class LockFreeStack {\n",[31,45,47],{"class":33,"line":46},3,[31,48,49],{}," private:\n",[31,51,53],{"class":33,"line":52},4,[31,54,55],{},"  struct Node;\n",[31,57,59],{"class":33,"line":58},5,[31,60,61],{},"  struct CountNodePtr {\n",[31,63,65],{"class":33,"line":64},6,[31,66,67],{},"    int external_count;\n",[31,69,71],{"class":33,"line":70},7,[31,72,73],{},"    Node* ptr;\n",[31,75,77],{"class":33,"line":76},8,[31,78,79],{},"  };\n",[31,81,83],{"class":33,"line":82},9,[31,84,85],{},"  struct Node {\n",[31,87,89],{"class":33,"line":88},10,[31,90,91],{},"    std::shared_ptr\u003CT> data;\n",[31,93,95],{"class":33,"line":94},11,[31,96,97],{},"    std::atomic\u003Cint> internal_count;\n",[31,99,101],{"class":33,"line":100},12,[31,102,103],{},"    CountNodePtr next;\n",[31,105,107],{"class":33,"line":106},13,[31,108,110],{"emptyLinePlaceholder":109},true,"\n",[31,112,114],{"class":33,"line":113},14,[31,115,116],{},"    Node(const T& value)\n",[31,118,120],{"class":33,"line":119},15,[31,121,122],{},"        : data(std::make_shared\u003CT>(value)), internal_count(0) {}\n",[31,124,126],{"class":33,"line":125},16,[31,127,79],{},[31,129,131],{"class":33,"line":130},17,[31,132,133],{},"  std::atomic\u003CCountNodePtr> head;\n",[31,135,137],{"class":33,"line":136},18,[31,138,139],{},"};\n",[10,141,142],{},[13,143,144,147,148,152],{},[28,145,146],{},"std::atomic\u003CCountNodePtr>","超过8字节，",[149,150,151],"del",{},"可能","并不是无锁的，主要学习如何通过计数避免资源过早释放",[154,155,156],"h3",{"id":156},"为什么引用计数",[13,158,159],{},"正常通过链表实现的无锁栈",[21,161,163],{"className":23,"code":162,"language":25,"meta":26,"style":26},"class LockFreeStack {\nprivate:\n  struct Node {\n    std::shared_ptr\u003CT> data;\n    Node* next;\n  };\n  std::atomic\u003CNode*> head;\n\npublic:\n  std::shared_ptr\u003CT> pop() {\n    Node* old_head = head.load(); \u002F\u002F 1\n    while(old_head && !head.compare_exchange_weak(old_head, old_head->next)); \u002F\u002F 2\n    std::shared_ptr\u003CT> res = old_head ? old_head->data : std::make_shared\u003CT>();\n    delete old_head;\n    return res;\n  }\n};\n",[28,164,165,169,174,178,182,187,191,196,200,205,210,215,220,225,230,235,240],{"__ignoreMap":26},[31,166,167],{"class":33,"line":34},[31,168,43],{},[31,170,171],{"class":33,"line":40},[31,172,173],{},"private:\n",[31,175,176],{"class":33,"line":46},[31,177,85],{},[31,179,180],{"class":33,"line":52},[31,181,91],{},[31,183,184],{"class":33,"line":58},[31,185,186],{},"    Node* next;\n",[31,188,189],{"class":33,"line":64},[31,190,79],{},[31,192,193],{"class":33,"line":70},[31,194,195],{},"  std::atomic\u003CNode*> head;\n",[31,197,198],{"class":33,"line":76},[31,199,110],{"emptyLinePlaceholder":109},[31,201,202],{"class":33,"line":82},[31,203,204],{},"public:\n",[31,206,207],{"class":33,"line":88},[31,208,209],{},"  std::shared_ptr\u003CT> pop() {\n",[31,211,212],{"class":33,"line":94},[31,213,214],{},"    Node* old_head = head.load(); \u002F\u002F 1\n",[31,216,217],{"class":33,"line":100},[31,218,219],{},"    while(old_head && !head.compare_exchange_weak(old_head, old_head->next)); \u002F\u002F 2\n",[31,221,222],{"class":33,"line":106},[31,223,224],{},"    std::shared_ptr\u003CT> res = old_head ? old_head->data : std::make_shared\u003CT>();\n",[31,226,227],{"class":33,"line":113},[31,228,229],{},"    delete old_head;\n",[31,231,232],{"class":33,"line":119},[31,233,234],{},"    return res;\n",[31,236,237],{"class":33,"line":125},[31,238,239],{},"  }\n",[31,241,242],{"class":33,"line":130},[31,243,139],{},[13,245,246,247,250,251,254],{},"主要基于出栈时，线程a运行完步骤1后，线程b删除了该节点。因此线程a中的",[28,248,249],{},"old_head","变成了\n悬空指针(并不是nullptr，但指向的空间被释放)，而步骤2中，调用CAS前需要计算实参",[28,252,253],{},"old_head->next","的值",[13,256,257],{},"因此在确保没有线程使用该结点前，不能释放该结点，引用计数可以解决该问题",[154,259,260],{"id":260},"为什么拆分引用计数",[10,262,263],{},[13,264,265],{},[266,267,268],"a",{"href":268,"rel":269},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F67371033\u002Fhow-does-the-split-reference-counting-work-in-a-lock-free-stack",[270],"nofollow",[13,272,273],{},[149,274,275],{},"还是看stackoverflow里老哥的回答吧，我也不明白为什么这么设计",[13,277,278],{},"将计数分为两个阶段",[280,281,282,306],"ol",{},[283,284,285,286,289,290,293,294],"li",{},"计数器被拆分(",[28,287,288],{},"external_count","与",[28,291,292],{},"internal_count",")，两个的和才是结点的真正计数",[295,296,297,300],"ul",{},[283,298,299],{},"引用结点时，外部计数加一，结束访问时，内部计数减一",[283,301,302,305],{},[28,303,304],{},"external_count == -internal_count","时，引用为0，删除结点",[283,307,308,309,312],{},"将外部计数加入到内部计数中，此时内部计数表示真正的计数",[310,311],"br",{},"内部计数为0时，删除结点",[13,314,315,316,319],{},"当其他线程引用结点时，外部计数加一。如果没有内部计数，在不再引用时，会递减计数，此时就需要\n与递增时一样(",[28,317,318],{},"IncreaseHeadCount()",")循环调用CAS",[17,321,322],{"id":322},"入栈",[280,324,325,335],{},[283,326,327,328,331,332],{},"将当前结点的",[28,329,330],{},"next","指向栈顶",[28,333,334],{},"head",[283,336,337,338],{},"将当前结点设置为",[28,339,334],{},[21,341,343],{"className":23,"code":342,"language":25,"meta":26,"style":26},"void push(const T& value) {\n  \u002F\u002F 1. 创建结点，并计数\n  CountNodePtr new_node;\n  new_node.ptr = new Node(value);\n  new_node.external_count = 1;\n\n  \u002F\u002F 2. 将新结点的next指向head\n  new_node.ptr->next = head;\n\n  \u002F\u002F 3. 确保新结点的next指向的是最新的head\n  \u002F\u002F 并将新节点设为head\n  while(!head.compare_exchange_weak(new_node.ptr->next, new_node));\n}\n",[28,344,345,350,355,360,365,370,374,379,384,388,393,398,403],{"__ignoreMap":26},[31,346,347],{"class":33,"line":34},[31,348,349],{},"void push(const T& value) {\n",[31,351,352],{"class":33,"line":40},[31,353,354],{},"  \u002F\u002F 1. 创建结点，并计数\n",[31,356,357],{"class":33,"line":46},[31,358,359],{},"  CountNodePtr new_node;\n",[31,361,362],{"class":33,"line":52},[31,363,364],{},"  new_node.ptr = new Node(value);\n",[31,366,367],{"class":33,"line":58},[31,368,369],{},"  new_node.external_count = 1;\n",[31,371,372],{"class":33,"line":64},[31,373,110],{"emptyLinePlaceholder":109},[31,375,376],{"class":33,"line":70},[31,377,378],{},"  \u002F\u002F 2. 将新结点的next指向head\n",[31,380,381],{"class":33,"line":76},[31,382,383],{},"  new_node.ptr->next = head;\n",[31,385,386],{"class":33,"line":82},[31,387,110],{"emptyLinePlaceholder":109},[31,389,390],{"class":33,"line":88},[31,391,392],{},"  \u002F\u002F 3. 确保新结点的next指向的是最新的head\n",[31,394,395],{"class":33,"line":94},[31,396,397],{},"  \u002F\u002F 并将新节点设为head\n",[31,399,400],{"class":33,"line":100},[31,401,402],{},"  while(!head.compare_exchange_weak(new_node.ptr->next, new_node));\n",[31,404,405],{"class":33,"line":106},[31,406,407],{},"}\n",[17,409,410],{"id":410},"出栈",[13,412,413,414,416,417,419],{},"出栈就是将栈顶(",[28,415,334],{},")的",[28,418,330],{},"设为栈顶，然后删除旧的栈顶",[21,421,423],{"className":23,"code":422,"language":25,"meta":26,"style":26},"void IncreaseHeadCount(CountNodePtr& old_counter) {\n  \u002F\u002F new_counter只是个临时量\n  counted_node_ptr new_counter;\n  do {\n    new_counter = old_counter;\n    ++new_counter.external_count;\n    \u002F\u002F 如果head没变，head的外部计数加一\n    \u002F\u002F 如果head改变，old_counter拷贝head，然后head的外部计数加一\n  } while(!head.compare_exchange_strong(old_counter, new_counter));\n  old_counter.external_count = new_counter.external_count;\n}\n\nstd::shared_ptr\u003CT> pop() {\n  CountNodePtr old_head = head.load();\n  while (1) {\n    \u002F\u002F 获取头节点以及其外部计数\n    IncreaseHeadCount(old_head);\n    node* old_head_ptr = old_head.ptr;\n    if (!old_head_ptr) return std::shared_ptr\u003CT>();\n\n    \u002F\u002F 某个线程(线程a)进入后，其他线程就无法获取到当前结点了\n    \u002F\u002F 其他线程就两种情况:\n    \u002F\u002F 1. 刚进入pop，还没有IncreaseHeadCount()，那就与当前节点无关了，加载的是另外的结点\n    \u002F\u002F 2. 经过了这个IncreaseHeadCount()，引用的是与线程a相同的结点，此时只能结束对当前结点的引用\n    \u002F\u002F 内部计数减一，即执行else if的内容\n    if (head.compare_exchange_strong(old_head, old_head_ptr->next)) {\n\n      std::shared_ptr\u003CT> res;\n      res.swap(old_head_ptr->data);\n\n      \u002F\u002F head不再引用该结点，因此减一\n      \u002F\u002F 当前的old_head也将要超出作用域，再次减一\n      \u002F\u002F 该代码好像有些误导，需要看下面的部分才能明白此处的含义\n      const int count_increase = old_head.external_count - 2;\n\n      \u002F\u002F 当前线程退出引用后，计数的第一阶段结束\n      \u002F\u002F 此时外部计数为external_count，而内部计数为internal_count - 2\n      \u002F\u002F 判断总计数 = external_count + (internal_count - 2)是否为0\n      \u002F\u002F 即判断internal_count == -(external_count - 2)\n      \u002F\u002F 此处解释了上面count_increase的定义，以及下面的if判断\n\n      \u002F\u002F 计数进入第二阶段，将外部计数加入到内部计数\n      \u002F\u002F internal_count = (internal_count - 2) + external_count\n      \u002F\u002F 也就是internal_count.fetch_add(external_count - 2)\n      if (old_head_ptr->internal_count.fetch_add(count_increase) ==\n          -count_increase) {\n        delete old_head_ptr;\n      }\n      return res;\n    } else if (old_head_ptr->internal_count.fetch_sub(1) == 1) {\n      \u002F\u002F internal_count.fetch_sub(-1)很好理解，其他线程退出对当前结点的引用，详情见if(CAS)处的注释\n      \u002F\u002F 对于internal_count == 1的判断\n      \u002F\u002F 一般来说internal_count肯定为0或者负数，即线程退出结点的引用而递减\n      \u002F\u002F 当internal_count为正数时，表示当前节点的计数由内部计数来表达(计数的第二阶段)\n      \u002F\u002F internal_count为1，表示只有当前线程持有该节点，因此退出引用，删除节点\n      delete old_head_ptr;\n    }\n  }\n}\n",[28,424,425,430,435,440,445,450,455,460,465,470,475,479,483,488,493,498,503,508,513,519,524,530,536,542,548,554,560,565,571,577,582,588,594,600,606,611,617,623,629,635,641,646,652,658,664,670,676,682,688,694,700,706,712,718,724,730,736,742,747],{"__ignoreMap":26},[31,426,427],{"class":33,"line":34},[31,428,429],{},"void IncreaseHeadCount(CountNodePtr& old_counter) {\n",[31,431,432],{"class":33,"line":40},[31,433,434],{},"  \u002F\u002F new_counter只是个临时量\n",[31,436,437],{"class":33,"line":46},[31,438,439],{},"  counted_node_ptr new_counter;\n",[31,441,442],{"class":33,"line":52},[31,443,444],{},"  do {\n",[31,446,447],{"class":33,"line":58},[31,448,449],{},"    new_counter = old_counter;\n",[31,451,452],{"class":33,"line":64},[31,453,454],{},"    ++new_counter.external_count;\n",[31,456,457],{"class":33,"line":70},[31,458,459],{},"    \u002F\u002F 如果head没变，head的外部计数加一\n",[31,461,462],{"class":33,"line":76},[31,463,464],{},"    \u002F\u002F 如果head改变，old_counter拷贝head，然后head的外部计数加一\n",[31,466,467],{"class":33,"line":82},[31,468,469],{},"  } while(!head.compare_exchange_strong(old_counter, new_counter));\n",[31,471,472],{"class":33,"line":88},[31,473,474],{},"  old_counter.external_count = new_counter.external_count;\n",[31,476,477],{"class":33,"line":94},[31,478,407],{},[31,480,481],{"class":33,"line":100},[31,482,110],{"emptyLinePlaceholder":109},[31,484,485],{"class":33,"line":106},[31,486,487],{},"std::shared_ptr\u003CT> pop() {\n",[31,489,490],{"class":33,"line":113},[31,491,492],{},"  CountNodePtr old_head = head.load();\n",[31,494,495],{"class":33,"line":119},[31,496,497],{},"  while (1) {\n",[31,499,500],{"class":33,"line":125},[31,501,502],{},"    \u002F\u002F 获取头节点以及其外部计数\n",[31,504,505],{"class":33,"line":130},[31,506,507],{},"    IncreaseHeadCount(old_head);\n",[31,509,510],{"class":33,"line":136},[31,511,512],{},"    node* old_head_ptr = old_head.ptr;\n",[31,514,516],{"class":33,"line":515},19,[31,517,518],{},"    if (!old_head_ptr) return std::shared_ptr\u003CT>();\n",[31,520,522],{"class":33,"line":521},20,[31,523,110],{"emptyLinePlaceholder":109},[31,525,527],{"class":33,"line":526},21,[31,528,529],{},"    \u002F\u002F 某个线程(线程a)进入后，其他线程就无法获取到当前结点了\n",[31,531,533],{"class":33,"line":532},22,[31,534,535],{},"    \u002F\u002F 其他线程就两种情况:\n",[31,537,539],{"class":33,"line":538},23,[31,540,541],{},"    \u002F\u002F 1. 刚进入pop，还没有IncreaseHeadCount()，那就与当前节点无关了，加载的是另外的结点\n",[31,543,545],{"class":33,"line":544},24,[31,546,547],{},"    \u002F\u002F 2. 经过了这个IncreaseHeadCount()，引用的是与线程a相同的结点，此时只能结束对当前结点的引用\n",[31,549,551],{"class":33,"line":550},25,[31,552,553],{},"    \u002F\u002F 内部计数减一，即执行else if的内容\n",[31,555,557],{"class":33,"line":556},26,[31,558,559],{},"    if (head.compare_exchange_strong(old_head, old_head_ptr->next)) {\n",[31,561,563],{"class":33,"line":562},27,[31,564,110],{"emptyLinePlaceholder":109},[31,566,568],{"class":33,"line":567},28,[31,569,570],{},"      std::shared_ptr\u003CT> res;\n",[31,572,574],{"class":33,"line":573},29,[31,575,576],{},"      res.swap(old_head_ptr->data);\n",[31,578,580],{"class":33,"line":579},30,[31,581,110],{"emptyLinePlaceholder":109},[31,583,585],{"class":33,"line":584},31,[31,586,587],{},"      \u002F\u002F head不再引用该结点，因此减一\n",[31,589,591],{"class":33,"line":590},32,[31,592,593],{},"      \u002F\u002F 当前的old_head也将要超出作用域，再次减一\n",[31,595,597],{"class":33,"line":596},33,[31,598,599],{},"      \u002F\u002F 该代码好像有些误导，需要看下面的部分才能明白此处的含义\n",[31,601,603],{"class":33,"line":602},34,[31,604,605],{},"      const int count_increase = old_head.external_count - 2;\n",[31,607,609],{"class":33,"line":608},35,[31,610,110],{"emptyLinePlaceholder":109},[31,612,614],{"class":33,"line":613},36,[31,615,616],{},"      \u002F\u002F 当前线程退出引用后，计数的第一阶段结束\n",[31,618,620],{"class":33,"line":619},37,[31,621,622],{},"      \u002F\u002F 此时外部计数为external_count，而内部计数为internal_count - 2\n",[31,624,626],{"class":33,"line":625},38,[31,627,628],{},"      \u002F\u002F 判断总计数 = external_count + (internal_count - 2)是否为0\n",[31,630,632],{"class":33,"line":631},39,[31,633,634],{},"      \u002F\u002F 即判断internal_count == -(external_count - 2)\n",[31,636,638],{"class":33,"line":637},40,[31,639,640],{},"      \u002F\u002F 此处解释了上面count_increase的定义，以及下面的if判断\n",[31,642,644],{"class":33,"line":643},41,[31,645,110],{"emptyLinePlaceholder":109},[31,647,649],{"class":33,"line":648},42,[31,650,651],{},"      \u002F\u002F 计数进入第二阶段，将外部计数加入到内部计数\n",[31,653,655],{"class":33,"line":654},43,[31,656,657],{},"      \u002F\u002F internal_count = (internal_count - 2) + external_count\n",[31,659,661],{"class":33,"line":660},44,[31,662,663],{},"      \u002F\u002F 也就是internal_count.fetch_add(external_count - 2)\n",[31,665,667],{"class":33,"line":666},45,[31,668,669],{},"      if (old_head_ptr->internal_count.fetch_add(count_increase) ==\n",[31,671,673],{"class":33,"line":672},46,[31,674,675],{},"          -count_increase) {\n",[31,677,679],{"class":33,"line":678},47,[31,680,681],{},"        delete old_head_ptr;\n",[31,683,685],{"class":33,"line":684},48,[31,686,687],{},"      }\n",[31,689,691],{"class":33,"line":690},49,[31,692,693],{},"      return res;\n",[31,695,697],{"class":33,"line":696},50,[31,698,699],{},"    } else if (old_head_ptr->internal_count.fetch_sub(1) == 1) {\n",[31,701,703],{"class":33,"line":702},51,[31,704,705],{},"      \u002F\u002F internal_count.fetch_sub(-1)很好理解，其他线程退出对当前结点的引用，详情见if(CAS)处的注释\n",[31,707,709],{"class":33,"line":708},52,[31,710,711],{},"      \u002F\u002F 对于internal_count == 1的判断\n",[31,713,715],{"class":33,"line":714},53,[31,716,717],{},"      \u002F\u002F 一般来说internal_count肯定为0或者负数，即线程退出结点的引用而递减\n",[31,719,721],{"class":33,"line":720},54,[31,722,723],{},"      \u002F\u002F 当internal_count为正数时，表示当前节点的计数由内部计数来表达(计数的第二阶段)\n",[31,725,727],{"class":33,"line":726},55,[31,728,729],{},"      \u002F\u002F internal_count为1，表示只有当前线程持有该节点，因此退出引用，删除节点\n",[31,731,733],{"class":33,"line":732},56,[31,734,735],{},"      delete old_head_ptr;\n",[31,737,739],{"class":33,"line":738},57,[31,740,741],{},"    }\n",[31,743,745],{"class":33,"line":744},58,[31,746,239],{},[31,748,750],{"class":33,"line":749},59,[31,751,407],{},[17,753,754],{"id":754},"内存模型",[13,756,757],{},[149,758,759],{},"这部分更是重量级，个人水平有限",[761,762,763],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":26,"searchDepth":40,"depth":40,"links":765},[766,770,771,772],{"id":19,"depth":40,"text":19,"children":767},[768,769],{"id":156,"depth":46,"text":156},{"id":260,"depth":46,"text":260},{"id":322,"depth":40,"text":322},{"id":410,"depth":40,"text":410},{"id":754,"depth":40,"text":754},"2023-08-13","笔记","md",{},"\u002Fblog\u002Flock-free-stack",{"title":5,"description":774},"blog\u002Flock-free-stack",[781,782],"C++","并发编程","8vUSAAA8Y1Zl1ZQxBd_eJfSc4-atxvH-UR1IzQ3VA6I",{"id":785,"title":786,"avatar":787,"body":788,"description":792,"extension":775,"meta":793,"name":795,"navigation":109,"path":796,"seo":797,"social":798,"stem":804,"__hash__":805},"home\u002Fhome.md","Home","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Favatar\u002Fjashinchan.jpg",{"type":7,"value":789,"toc":790},[],{"title":26,"searchDepth":40,"depth":40,"links":791},[],"Programming enthusiast, maybe.",{"layout":794},"page","阿东","\u002Fhome",{"title":786,"description":792},{"github":799,"email":800,"bilibili":801,"qq":802,"rss":803},"https:\u002F\u002Fgithub.com\u002Fstarrobe","mailto:starrobe@163.com","https:\u002F\u002Fspace.bilibili.com\u002F382631863","tencent:\u002F\u002Fmessage\u002F?uin=2604335528","\u002Ffeed.xml","home","XHOrpn2fXb8x87SMsqZTGaANCESyH9qV8TDm8rnQewI",1777128585659]