专利名称:在线性栈上高效恢复协同例程的制作方法
在线性栈上高效恢复协同例程
背景技术:
在计算机科学中,协同例程是通常概括子例程并且可允许多个入口点以进行挂起 和恢复执行的程序组件。协同例程有时被用于实现协作任务、迭代器、无限列表和管道。虽 然子例程通常是后进先出构造,但协同例程的寿命可按其用途来规定。虽然子例程只能返 回一次,但协同例程可返回或让步一次以上。虽然子例程的开始通常是其唯一入口点,但协 同例程的开始是第一入口点且后续入口点可跟随让步(yield)命令。例如,同子例程一样, 在协同例程中让步可将结果和控制返回给调用方协同例程,但不同于子例程,在该协同例 程下次被调用时,执行可刚好在让步调用之后开始而非在协同例程开头开始。由于协同例程可返回多次,因此有可能在后续调用时返回附加的值。其中后续调 用产生附加结果的协同例程常常被称为生成器。传统子例程使用可在程序执行开头被预分 配的单个栈。相反,由于协同例程能够作为对等体调用其他协同例程,因此通常为协同例程 分配附加的栈。有时,预分配栈或者高速缓存预先分配的栈。生成器还一般化子例程,并且常常被用于简化迭代器的编写。生成器中的让步语 句通常不指定要跳转到的协同例程,而是将值回传给父例程。然而,仍然有可能在顶级分派 器例程的辅助下在生成器设施上实现协同例程,顶级分派器例程将控制显式地传递给从生 成器传回的令牌所标识的子生成器。许多流行编程语言(包括C、C++和C#)由于基于栈的子例程实现的限制因而在该 语言内不直接支持协同例程。在其中协同例程将在逻辑上被用于在可能的情况下实现一机 制的情形中,通常创建使用布尔标志和其他状态变量的组合来在调用之间维护内部状态的 子例程。条件语句导致基于状态变量的值而执行不同代码路径。或者,实现开关语句形式 的显式状态机。提供使用协同例程的替换方案的方法是使用线程。线程提供用于管理基本上同时 执行的代码片断的实时协作交互的能力。线程通常被优先调度。协同例程通常不是。由于 线程可在任何时刻被重新调度且能并发地执行,因此使用线程的程序必须注意锁定。相反, 由于协同例程只能在程序中的特定点被重新调度并且不并发地执行,因此使用协同例程的 程序常常完全避免了锁定问题。在高级语言中实现协同例程的一种方法放弃了便携性。相反,处理器系列专用的 实现对于函数是用汇编语言编写的,以节约和恢复协同例程上下文。必须注意这些函数的 使用和编写,从而在协同例程共享相同栈时位于该栈上的变量不被覆写。因此,通常对于高 级语言中基于栈的协同例程,需要函数来创建替换栈并在替换栈之间跳转。可提供第三机 器专用函数以创建用于新协同例程的上下文。传统上,协同例程的栈大小是固定的且在执 行期间不能增长。通常,程序分配比所需的更大的栈以避免潜在的栈溢出。概述在使用单个线性机器调用栈在没有直接协同例程支持的语言中执行的计算机系 统中,驱动程序例程控制协同例程执行。当协同例程最初被另一协同例程调用时,被调用的 协同例程被放到栈上调用者的帧之上。协同例程被首次挂起时,其所有栈帧向下直至驱动程序栈帧并且包括驱动程序栈帧被复制到堆。然而,若协同例程被再次挂起,则其栈帧不被 再次复制到堆。当协同例程恢复时,驱动程序例程仅将叶例程放到栈上。尽管该方法中的 单个挂起对于深度d的栈将花费0(d),但由于每一帧仅被复制到堆一次,因此该方法的总 分摊成本为0 (m),其中m是曾挂起的帧数,且m通常小于被调用的总帧数η。若在叶例程中抛出异常,则驱动程序例程检测或抓住该异常。驱动程序在栈上用 与调用者相关联的栈帧来代替与被调用者(被调用)叶例程相关联的栈帧。驱动程序执行 调用方例程,并且异常被再次抛出。提供本概述以便以简化形式介绍将在以下详细描述中进一步描述的一些概念。本 概述并非旨在标识出要求保护的主题的关键特征或必要特征,亦非旨在用于限定要求保护 的主题的范围。附图简述在附图中
图1示出根据本文所公开主题的各方面用于在线性栈上高效恢复协同例程的系 统100的示例;图2是根据如关于图1所描述的本文所公开主题的各方面用于在线性栈上高效恢 复协同例程的方法200的示例的流程图。图3是示出其中可实现本文所公开主题的各方面的计算环境的示例的框图;以及图4是根据本文所公开主题的各方面的集成开发环境的示例的框图。详细描述概览根据本文所公开主题的各方面,未挂起协同例程由调用栈机制来处理,其中该栈 随着作出调用和从调用返回而增长和收缩。然而,在协同例程首次挂起时,执行附加的调用 栈处理。发出挂起消息,并且调用栈的整个能恢复部分被从机器调用栈移除并被复制到堆。 将控制返回给驱动程序方法(恢复器)的帧被复制到调用栈,从而恢复不会递归地重新激 活整个调用栈。相反,恢复器仅重新激活被称为叶帧的最顶部或最当前帧。在协同例程挂 起时,其不返回到其调用者,而是返回到已重新激活它的恢复器,该恢复器检索将控制返回 到的合适地址。由编译器执行的状态机转换不循环以重复地重新激活被调用者直至被调用栈返 回。从调用方状态机的观点而言,当被调用者返回时,其要么同步地返回,在这种情形中,结 果(或异常)被自然地消耗且处理继续进行;要么返回包括挂起,在这种情形中,处理被挂 起,且期望处理将不恢复直至被调用者完成并且恢复器返回其结果。在线性栈上高效恢复协同例程术语协同例程是用于动作序列的通用术语。协同例程类似于程序,除了与程序不 同的是,协同例程可在执行期间被暂停(挂起)并在以后恢复。恢复可通过客户机请求来 自协同例程的输出来发起。例如,在一些编程语言中,迭代器可被实现为协同例程,其任务 是按需产生集合的元素。一旦该协同例程已产生集合的一元素,其暂停。在需要下一个元 素时,该协同例程恢复。该过程继续,直至不需要更多元素或者直至已产生了集合中的所有 元素。恢复可通过输入变得可用来驱动或发起。例如,协同例程可在所请求数据不可用时暂停,这可能在较长等待时间通道上请求数据(例如,从盘读取或者从网络访问信息等) 时发生。恢复可由回调或事件发起或者可在所请求数据变得可用时以其他方式触发。这种 类型的情景很可能在采用异步编程技术时遇到。通常,当一个程序调用另一个程序时,调用方程序等待结果并随后调用下一个程 序,等待其结果,依此类推。相反,可从另一协同例程递归地调用一个或多个协同例程达到 无论任何递归深度。在第一协同例程在递归协同例程调用链中调用第二协同例程时,当第 二协同例程暂停时,第一协同例程暂停,依此类推。递归程序调用和子例程调用在传统上是经由称为机器调用栈(也称为执行栈、控 制栈、函数栈或运行时栈)的低级数据结构来实现的。调用栈保持跟踪每个有效被调用者 在完成执行时将控制返回到的点。活跃被调用者是已被调用但尚未通过返回调用者来完成 执行的被调用者。被调用者使调用者的返回地址进栈,并且在被调用者完成时,被调用者使 该返回地址出栈并将控制转移到该出栈的地址。因此,被放到栈上的信息随着程序执行而 增长和收缩。所描述的调用栈自动支持串行世界中的递归。取决于语言、操作系统和机器环 境,调用栈可服务附加函数,包括存储本地数据、参数、逻辑运算、指向当前实例的指针(该 指针(this pointer))、外围例程上下文和返回状态。机器调用栈数据结构由栈帧构成,栈帧是包含被调用者状态信息的取决于机器的 数据结构。每个栈帧对应于对子例程的调用,该子例程尚未通过返回而终止。调用栈顶部 的栈帧(叶帧)保持关于当前执行的子例程的信息。由子例程调用指令提供给被调用者的 返回地址通过被调用者将该值压到调用栈上来保存。在被调用者返回时,整个栈帧出栈并 且控制被返回给位于返回地址处的指令。通常,一个调用栈与进程的每个任务或线程相关 联。在高级编程语言环境中,通常不允许程序员操纵调用栈的存储器。通常,程序员仅 能访问将在栈上执行的函数的列表。C和C++的实现将单个线性(连续)栈用作栈帧的存 储。程序调用使返回地址进栈,并且被调用者将栈上的空间分配给其自己的本地变量等。线 性栈机制在串行世界中高效地工作。然而,线性栈固有地不能处理以非串行方式挂起和恢 复的协同例程。因此,为了在不支持在机器调用栈级挂起和恢复协同例程的平台上实现协 同例程调用,有时采用处理协同例程挂起和恢复的附加处理。在使用单个线性机器调用栈在没有直接协同例程支持的语言中执行的计算机系 统的协同例程的传统实现中,通过将机器调用栈上与协同例程相关联的栈帧存储到模仿该 机器调用栈的堆中来挂起协同例程。堆是保留用于对象的存储器区域并且与机器调用栈分 开。在协同例程恢复时,与该协同例程相关联的所有栈帧从堆重构到机器调用栈上。因此, 当机器调用栈上与协同例程相关联的协同例程深度为d时,执行协同例程的一个步骤(恢 复它,执行其计算的一些部分,并随后再次将它挂起)的时间至少为0(d)。若协同例程不被 挂起,则机器调用栈上与该协同例程相关联的栈帧不被复制到堆。另一种已知方法在协同例程每次被调用时(而非等待直至协同例程被挂起)将该 协同例程的栈帧从机器调用栈复制到模仿该机器调用栈的堆。驱动程序例程控制协同例程 执行。在该方法中,在任何给定时间,与协同例程相关联的仅两个栈帧在机器调用栈上。在 机器调用栈上的与协同例程相关联的这两个栈帧为驱动程序例程和代表协同例程的叶例 程的执行的栈帧。叶例程是与协同例程相关联的最顶部栈帧。在叶例程挂起时,最顶部栈帧被从机器调用栈移除并被存储到堆上。在叶例程的执行恢复时,该叶例程的所存储栈帧 被重构到机器调用栈上。在叶例程完成执行并返回其调用者时,驱动程序例程将调用者的 栈帧放到栈顶作为新叶。若任何当前执行的叶例程调用另一个例程,则驱动程序例程在调 用栈上用代表被调用者(被调用例程)的新叶例程来代替当前执行的叶例程。驱动程序在 堆中保持跟踪谁调用谁以及协同例程被调用的次序。第二种方法允许协同例程在恒定的时 间里被调用、挂起、恢复和返回。然而,所有协同例程栈帧被复制到堆至少一次,即使协同例 程不被挂起。根据本文所公开主题的各方面,帧仅在它们实际上曾被挂起时才被复制到堆。而 且,给定帧仅被复制到堆一次。驱动程序例程在栈的协同例程部分的底部。当协同例程最 初被另一协同例程调用时,被调用协同例程将不在栈上代替其调用者,(从而被调用协同例 程的帧随后将直接位于驱动程序例程之上)。相反,被调用例程将以正常子例程的方式被调 用,且被调用例程的帧被放在栈上调用者的帧之上。当首次挂起时,栈上向下直至驱动程序 例程的每一帧皆被复制到堆。在恢复时,驱动程序例程仅将叶例程放回栈上,并且通过在栈 上用调用者帧代替被调用者帧来处理返回和抛出异常。尽管该方法中的单个挂起对于深度 d的栈将花费0(d),但由于每一帧仅被复制到堆一次,因此该方法的总分摊成本为0(m),其 中m是曾挂起的帧数。m通常小于被调用的总帧数η。图1示出根据本文所公开主题的各方面用于在线性栈上高效恢复协同例程的系 统100的示例。系统100的所有或部分可驻留在诸如以下参照图3描述的计算机之类的一 个或多个计算机上。系统100的所有或部分可驻留在诸如以下参照图4描述的计算机之类 的一个或多个软件开发计算机(例如,计算机102)上。系统100或其部分可包括诸如以下 参照图4描述和示出的集成开发环境(例如,IDE 104)的一部分。或者,系统100或其部 分可被提供作为独立系统或作为插件或附件。系统100可包括以下各项中的一个或多个处理器(诸如处理器142)、存储器 144、和用于在线性栈上高效恢复协同例程的模块106。本领域公知的其他组件也可被包括 但不在这里示出。将理解,用于在线性栈上高效恢复协同例程的模块106可被加载到存储 器144中以使得诸如处理器142之类的一个或多个处理器执行归因于用于在线性栈上高效 恢复协同例程的模块106的动作。根据本文所公开主题的各方面,用于在线性栈上高效恢 复协同例程的模块106可接收或生成关于协同例程的挂起或恢复的通知。当协同例程最初 被另一协同例程调用时,被调用的协同例程的栈帧被放到栈上调用者的帧之上。若协同例 程未曾被挂起,则与该协同例程相关联的帧不被复制到堆。若协同例程被挂起,则在首次挂 起时,与被挂起协同例程相关联的所有栈帧向下直至驱动程序栈帧并且包括驱动程序栈帧 被复制到堆。然而,后续挂起不导致先前被复制的栈帧被再次复制到堆。当协同例程恢复 时,驱动程序例程仅将叶例程放到栈上。返回和抛出异常通过在栈上用调用者帧代替被调 用者帧来处理。用于在线性栈上高效恢复协同例程的模块106可包括以下各项中的一个或多个 编译器114,诸如后台编译器、并行编译器或增量编译器;解析器,诸如后台解析器、并行解 析器或增量解析器;或者插件、预处理器、或附件;或者对IDE、解析器、编译器或预处理器 的扩展。用于在线性栈上高效恢复协同例程的模块106可被附连到以下各项、合并到以 下各项内或与以下各项相关联编译器,诸如后台编译器、并行编译器或增量编译器;解析器,诸如后台解析器、并行解析器或增量解析器;或者插件、预处理器、或附件;或者对IDE、 解析器、编译器或预处理器的扩展。调用者110可代表程序、子例程或调用方协同例程。被调用者112可代表被调用 的协同例程、程序或子例程。用于在线性栈上高效恢复协同例程的模块106可监视调用者 110与被调用者112之间的调用。用于在线性栈上高效恢复协同例程的模块106可执行处 理以在利用单个线性机器调用栈的计算机环境中挂起和恢复协同例程,该单个线性机器调 用栈本身固有地不能挂起和恢复协同例程。用于在线性栈上高效恢复协同例程的模块106 可监视协同例程处理。仅响应于检测到协同例程(例如,被调用的协同例程)的首次挂起, 单个线性机器调用栈的能恢复部分被保存到分开的数据结构(诸如堆)中。协同例程被首 次挂起时,其所有栈帧向下直至驱动程序栈帧并且包括驱动程序栈帧被复制到堆。然而,若 协同例程被再次挂起,则其栈帧不被再次复制到堆中。用于调用方协同例程和被调用协同 例程的该单个线性机器调用栈的能恢复部分可包括包含调用者的返回地址的栈帧、包含被 调用协同例程的返回地址的栈帧以及驱动程序例程的栈帧。在递归协同例程调用链的情形中,线性机器调用栈的能恢复部分可包括包含到深 度d的所有被递归调用的协同例程的所有返回地址的所有栈帧。单个线性机器调用栈的能 恢复部分可被保存到诸如但不限于堆数据结构之类的数据结构中。来自线性机器调用栈的 栈帧被保存到其中的数据结构可与该线性机器调用栈分开。在发生挂起时,线性机器调用栈的能恢复部件或部分可被移除,并且恢复器栈帧 (包括恢复器的返回地址)可被复制到线性机器调用栈上,以使得当协同例程恢复并且控 制返回到调用栈上的最顶部栈帧时,控制被传递到恢复器108。当被调用的协同例程恢复 时,只有包括被调用协同例程的返回地址的栈帧才能被复制到该单个线性机器调用栈上, 而不将所有被保存的能恢复部分都重构到该单个线性机器调用栈上。响应于检测到暂停协 同例程(例如,被调用的协同例程)的恢复,线性机器调用栈上的恢复器栈帧可被处理并且 恢复器108可被调用。响应于检测到被调用协同例程的恢复,被调用协同例程可被执行,从 而通过将控制返回到放在该单个线性机器调用栈上的关于恢复器的返回地址来将控制返 回给恢复器。恢复器108可通过将调用者的栈帧复制到调用栈上来将控制返回到关于调用 者的栈帧中的返回地址。即,在协同例程恢复时,恢复器108可仅为合适协同例程将最顶部 叶帧复制到调用栈,而不重构整个调用栈。该过程可继续,直至整个调用链已完成。当协同 例程返回其调用者时,被调用者栈帧被调用者栈帧代替。在抛出异常时,根据本文所公开主题的各方面,可抓住该异常,被调用者的栈帧可 被调用者的栈帧代替,并且随后可重新抛出该异常。图2示出根据本文所公开主题的各方面用于在线性栈上高效恢复协同例程的方 法200的示例。在202,协同例程(调用者)调用另一协同例程(被调用者)。在204,若被 调用协同例程从未挂起,则栈不被复制到堆006)。即,被调用例程的帧被放在栈上调用者 的帧之上并且通过传统的机器调用栈处理来处理。在204,若发生被调用协同例程的挂起, 则在208,机器栈的能恢复部分若先前尚未被复制到堆则被复制到堆上。被调用者首次被挂 起时,与被挂起协同例程相关联的所有栈帧向下直至驱动程序栈帧并且包括驱动程序栈帧 被复制到堆。然而,后续挂起不导致先前被复制的栈帧被再次复制到堆。恢复器栈帧被压 到调用栈上。恢复器调用栈在被处理时将控制返回到恢复器而非返回到调用者。当或若被调用者在210恢复时,恢复器在212被调用,并且恢复器在212仅将被调用者的叶帧(最顶 部栈帧)从堆重构到栈上并且处理继续。在214,若抛出异常或者若被调用者返回,则栈上 的被调用者帧被调用者栈帧代替并且处理继续。例如,假设协同例程实现按需产生集合结果的迭代器。该集合可由二进制树数据 结构表示,其中该二进制树中的每个节点包括包含该集合的成员的值。为了以二进制树规 定的指定次序产生该集合的元素,该二进制树可通过协同例程的递归调用来遍历。例如,假 设二进制树表示元素1、2和3的集合。为了以该顺序产生元素1、2、3,二进制树的根节点通 常可具有值2,根节点的左孩子节点可具有值1且没有左或右子树,而根节点的右孩子节点 可具有值3且没有左或右子树。迭代器首先产生根节点的左树的值,随后从根节点产生值, 然后产生右树的元素。该过程利用递归协同例程。在产生了每个元素之后,协同例程暂停, 直至该集合的下一元素被请求。当调用方例程在二进制树上调用迭代器函数时,调用者的地址进栈。迭代器在二 进制树的根上调用第一协同例程。第一协同例程的下一指令的地址被放在栈上。向根节点 的左树作出第二递归协同例程调用。在根节点的左树上调用的第二协同例程的地址被放在 栈上。由于根节点的左树没有子树,因此不向左树的左孩子节点作出递归调用。产生存储 在左树中的该集合的元素(1)。在左节点上调用的协同例程挂起。在根上的协同例程也挂 起。当树的左孩子节点上的协同例程挂起时,有2帧(在左根上调用的协同例程和在 根上调用的协同例程的地址)的调用栈被保存在堆中。恢复器帧被复制到栈上。当协同例 程由于要求该集合的元素的客户机要求下一元素而恢复时,并非从堆取出两个所保存帧并 将它们放回到栈上,而是仅将与左树上(其中有1的节点上)的协同例程调用相对应的最 顶部帧复制到栈中。因此,栈上有一帧,其中栈上的该帧对应于在左节点上被调用的协同例 程。第二调用确定其中有1的节点没有右树。结果将被返回到的协同例程是在根节点上调用的协同例程,但该帧没有被复制回 栈上,因此并非如传统模型中那样执行栈返回,而是协同例程返回到恢复器帧(其为栈上 的下一帧)上的地址。因此,恢复通过恢复器例程来引导。恢复器确定根的返回地址并引 导将结果返回该地址。在根节点上调用的协同例程产生其值( 并再次挂起。客户机下次 要求值时,恢复器被再次调用。恢复器将根帧放在栈上,因为这是自先前开始就挂起的帧。 在根节点上调用的协同例程从其产生其自己的值之后的指令开始运行。接下来,在右分支 上作出传统的递归协同例程调用以产生右子树中的任何值。此时,调用栈上有三帧底部为恢复器帧,其上是关于在根上调用的协同例程的 帧,顶部是关于在右叶上调用的协同例程的帧。右叶确定其没有左分支,且将产生其自己的 值(3)并且挂起。在挂起时,最顶部的两帧被存储在堆中并且恢复器帧被复制到栈上。当 客户机要求另一元素时,发生恢复。最顶部的帧引导至恢复器的处理。恢复器将与右叶相 对应的帧放在栈上。在右叶上调用的协同例程确定其没有右子树。其将结果返回给恢复 器,表明其完成。恢复器将根帧放在栈上。根上的协同例程恢复。它确定其完成并且返回 到驱动程序。栈上的下一帧是调用者的返回地址。调用者接收关于集合中没有更多元素的 消息。将理解,以上描述的方法可通过向恢复器产生关于接下来运行哪个协同例程的指令来应用于具有多个入口点的协同例程。还将理解,以上描述的过程可提供对异步编程的 支持。例如,假设异步例程按节读取文件。该例程可在循环中执行,从而递归地调用读取文 件的一部分的另一例程。为了避免使线程总是在运行,主例程可被挂起直至读取文件的一 部分的协同例程结束。挂起的主例程可在读取文件的一部分的协同例程返回结果时恢复。 异步主例程和读取文件的一部分的协同例程两者被挂起直至文件的一部分返回。带回文件 内容的线程恢复请求信息的例程,将文件内容给它并且它将内容传回给主例程。该过程被重复,从而文件的另一节被另一递归调用检索,依此类推。在这种情形 中,通过从文件带回内容的操作系统事件来发起恢复。在这种情形中,当操作系统带回文 件内容时,其执行对早先安装在栈中的恢复器的回调。恢复器通过回到栈上的最新叶例程 (读取文件的节的例程)并从其所到的地方执行它来如上所述地恢复协同例程。当叶例程 完成时,其返回到恢复器并且恢复器返回到原始调用者。执行继续,直至文件的下一节被请 求,此时被调用例程和调用方例程被再次挂起,依此类推。合适计算环境的示例为了提供有关本文所公开的主题的各个方面的上下文,图3以及以下讨论旨在提 供其中可以实现各实施例的合适计算环境510的简要概括描述。尽管本文所公开的主题是 在诸如程序模块等由一个或多个计算机或其它计算设备执行的计算机可执行指令的通用 上下文中描述的,但本领域技术人员将认识到,本文所公开的主题的各部分还能够结合其 它程序模块和/或硬件和软件的组合来实现。通常,程序模块包括执行特定任务或实现特 定数据类型的例程、程序、对象、物理人为产物、数据结构等。通常,程序模块的功能可以在 各个实施例中按需进行组合或分布。计算环境510只是合适的操作环境的一个示例,并且 不旨在对此处所公开的主题的使用范围或功能提出任何限制。参照图3,描述了计算机512形式的用于线性栈上高效恢复协同例程的计算设备。 计算机512可包括处理单元514、系统存储器516和系统总线518。处理单元514可以是各 种可用处理器中的任何一种。也可以使用双微处理器及其他多处理器体系结构作为处理单 元514。系统存储器516可包括易失性存储器520和非易失性存储器522。非易失性存储 器522可包括只读存储器(ROM)、可编程ROM(PROM)、电可编程ROM(EPROM)或闪存。易失性 存储器520可包括可充当外部高速缓冲存储器的随机存取存储器(RAM)。系统总线518将 系统物理人为产物(包括系统存储器516)耦合到处理单元514。系统总线518可以是几种 类型的总线结构中的任何一种,包括存储器总线、存储控制器、外围总线、外部总线或局部 总线,并且可以使用各种可用总线体系结构中的任一种。计算机512通常包括各种计算机可读介质,诸如易失性和非易失性介质、可移动 和不可移动介质。计算机存储介质可以通过用于存储诸如计算机可读指令、数据结构、程 序模块或其它数据等信息的任何方法或技术来实现。计算机存储介质包括但不限于,RAM、 ROM、EEPR0M、闪存或其它存储器技术、CDR0M、数字多功能盘(DVD)或其它光盘存储、磁盒、 磁带、磁盘存储或其它磁存储设备、或可以用来储存所期望的信息并可由计算机512访问 的任何其他非瞬态介质。将理解,图3描述了可充当用户与计算机资源之间的媒介的软件。该软件可以包 括可存储在盘存储5M上的操作系统528,该操作系统可控制并分配计算机系统512的资 源。盘存储5M可以是通过诸如接口 5 的不可移动存储器接口连接到系统总线518的硬盘驱动器。系统应用程序530利用由操作系统5 通过存储在系统存储器516中或者存储 在盘存储5M上的程序模块532和程序数据534对资源的管理。应该明白,计算机可用各 种操作系统或操作系统的组合来实现。用户可通过输入设备536向计算机512输入命令或信息。输入设备536包括但不 限于定点设备,诸如鼠标、跟踪球、指示笔、触摸垫、键盘、话筒等。这些及其他输入设备通过 系统总线518经由接口端口 538连接到处理单元514。接口端口 538可表示串行端口、并行 端口、通用串行总线(USB)等。输出设备540可与输入设备使用相同类型的端口。提供输 出适配器M2以举例说明存在像监视器、扬声器、以及打印机的需要特定适配器的一些输 出装置M0。输出适配器542包括但不限于,在输出设备540和系统总线518之间提供连接 的视频卡和声卡。其他设备和/或设备系统,诸如远程计算机M4,可提供输入和输出两种 能力。计算机512可以使用到诸如远程计算机544之类的一个或多个远程计算机的逻辑 连接来在联网环境中操作。远程计算机544可以是个人计算机、服务器、路由器、网络PC、对 等设备或其它常见的网络节点,并且通常包括许多或所有以上相对于计算机512所描述的 元件,尽管在图4中仅示出了存储器存储设备M6。远程计算机544可经由通信连接550逻 辑地连接。网络接口 548涵盖诸如局域网(LAN)和广域网(WAN)这样的通信网络,但也可 包括其他网络。通信连接550是指用来将网络接口 548连接到总线518的硬件/软件。连 接550可以在计算机512内部或外部并且包括诸如调制解调器(电话、电缆、DSL和无线) 和ISDN适配器、以太网卡等内部和外部技术。应该理解,所示网络连接仅是示例,并且可以使用在计算机之间建立通信链路的 其它手段。本领域的普通技术人员可以理解,计算机512或其它客户机设备可作为计算机 网络的一部分来部署。在这一点上,本文所公开的主题涉及具有任意数量的存储器或存储 单元以及在任意数量的存储单元或卷上发生的任意数量的应用程序和进程的任何计算机 系统。本文所公开的主题的各方面可应用于具有部署在网络环境中的具有远程或本地存储 的服务器计算机和客户计算机的环境。本文所公开的主题的各方面也可应用于具有编程语 言功能、解释和执行能力的独立计算设备。图4示出集成开发环境(IDE) 600和通用语言运行时环境602。IDE 600可允许用 户(例如,开发者、程序员、设计者、编码者等)在计算机系统中设计、编码、编译、测试、运 行、编辑、调试或构建程序、程序集、网站、web应用程序和web服务。软件程序可包括以一种 或多种源代码语言(例如,VisualBasic、Visual J#、C++、C#、J#、Java Script,APL,COBOL, Pascal、Eiffel、Haskell、ML、Oberon> Perl、Python、Scheme、Smalltalk 等)创建的源代 码(组件610)。IDE 600可提供本机代码开发环境,或者可提供在虚拟机上运行的托管代 码开发,或者可提供其组合。IDE 600可提供使用.NET框架的托管代码开发环境。可使用 语言专用源编译器620从源代码组件610和本机代码组件611创建中间语言组件650,并且 在执行应用时使用中间语言编译器660(例如,即时(JIT)编译器)从中间语言组件650创 建本机代码组件611(例如,机器可执行指令)。即,当IL应用被执行时,其在被执行的同时 被编译成适合正在其上执行它的平台的合适机器语言,藉此使代码能跨若干平台便携。或 者,在其他实施例中,程序可被编译成适合其目标平台的本机代码机器语言(未示出)。用户可根据已知软件编程技术以及与特定源语言相关联的特定逻辑和句法规则经由IDE 600中的用户接口 640和源代码编辑器651来创建和/或编辑源代码组件。此后, 源代码组件610可经由源编译器620被编译,藉此可创建该程序的中间语言表示,诸如汇编 630。汇编630可包括中间语言组件650和元数据642。应用程序设计可以能够在部署前被 验证。此处所述的各种技术可结合硬件或软件,或在适当时以其组合来实现。由此,本文 所公开的方法和装置或其特定方面或部分可采取包含在诸如软盘、CD-ROM、硬盘驱动器或 任何其它机器可读存储介质等有形介质中的程序代码(即,指令)的形式,其中当程序代码 被加载到诸如计算机等机器内并由其执行时,该机器成为用于实现本文所公开的主题的各 方面的装置。在程序代码在可编程计算机上执行的情况下,计算设备通常将包括处理器、该 处理器可读的存储介质(包括易失性和非易失性的存储器和/或存储元件)、至少一个输入 设备、以及至少一个输出设备。可例如通过使用数据处理API等来利用域专用编程模型各 方面的创建和/或实现的一个或多个程序可用高级过程语言或面向对象的编程语言来实 现以与计算机系统通信。然而,如果需要,该程序可以用汇编语言或机器语言来实现。在任 何情形中,语言可以是编译语言或解释语言,且与硬件实现相结合。虽然已结合附图描述了本文所公开的主题,但是应理解,可作出修改以按不同方 式执行相同功能。
权利要求
1.一种系统(100),包括处理器(14 和存储器(144),所述存储器包括配置成使所述处理器(14 执行以下动 作的模块(106)在利用固有地不能挂起和恢复协同例程的单个线性机器调用栈的计算机环境中挂起 和恢复包括调用方协同例程(110)和被调用协同例程(112)的协同例程,其中仅响应于检测到所述被调用协同例程的挂起,所述模块保存先前未保存的所述单个线性机器调用栈的能恢复部分,所述单个线性机器调用栈 的所述能恢复部分包括所述调用方协同例程的栈帧、所述被调用协同例程的栈帧以及恢复 器的栈帧;将所述单个线性机器调用栈的所述能恢复部分从所述单个线性机器调用栈移除;将恢复器的返回地址放到所述单个线性机器调用栈上;以及响应于检测到所述被调用 协同例程的恢复仅将所述被调用协同例程的所述栈帧复制到所述单个线性机器调用栈上,而不将所述 单个线性机器调用栈的所有所述能恢复部分复制到所述单个线性机器调用栈上。
2.如权利要求1所述的系统,其特征在于,响应于检测到所述被调用协同例程的恢复, 通过将控制返回到放在所述单个线性机器调用栈上的所述恢复器的所述返回地址来将控 制返回给所述恢复器,所述恢复器将所述被调用协同例程的所述栈帧放到所述单个线性机 器调用栈上。
3.如权利要求1所述的系统,其特征在于,响应于抛出异常,在所述单个线性机器调用 栈上所述被调用协同例程的所述栈帧被所述调用方协同例程的所述栈帧代替。
4.如权利要求1所述的系统,其特征在于,通过所述调用方协同例程请求附加信息来 发起恢复。
5.如权利要求1所述的系统,其特征在于,通过返回所述调用方协同例程所请求的信 息来发起恢复。
6.一种方法000),包括在其中实现线性机器调用栈的软件开发计算机上执行的编程环境中,其中所述线性机 器调用栈固有地不能挂起和恢复协同例程响应于接收到从调用者对被调用协同例程的未挂起协同例程调用,不将与所述被调用 协同例程相关联的栈帧从所述线性机器调用栈保存到堆O06);以及仅响应于检测到从所述调用者调用的被调用协同例程的首次挂起,将与所述被调用协 同例程相关联的栈帧从所述线性机器调用栈保存到堆(208)。
7.如权利要求6所述的方法,其特征在于,还包括响应于检测到所述被调用协同例程的恢复,从所述堆向所述线性机器栈重构仅叶帧。
8.如权利要求6所述的方法,其特征在于,还包括实现高级编程语言的迭代器或实现异步编程。
9.如权利要求6所述的方法,其特征在于,还包括响应于所述被调用协同例程返回,用所述调用者的栈帧来代替所述线性机器调用栈上 所述被调用协同例程的所述栈帧。
10.如权利要求6所述的方法,其特征在于,还包括响应于抛出异常,抓住所述异常,在所述线性机器调用栈上用所述调用者的栈帧来代 替所述被调用协同例程的所述栈帧并重新抛出所述异常。
11.一种包括计算机可执行指令的计算机可读存储介质,所述指令在被执行时使至少 一个处理器(142)执行对未挂起协同例程的线性机器调用栈处理;除所述线性机器调用栈处理以外,通过以下操作对被挂起协同例程执行调用栈处理 将包括所述被挂起协同例程的返回地址的栈帧、包括所述调用者的返回地址的栈帧以 及驱动程序例程的栈帧保存到与所述线性机器调用栈分开的数据结构中; 挂起所述调用者;从所述线性机器调用栈移除所述调用者的所述栈帧和所述被挂起协同例程的所述栈帧;将恢复器的返回地址放到所述线性机器调用栈上;以及响应于接收到所述被挂起协同 例程的恢复将所述调用者的所述栈帧复制到所述线性机器调用栈上。
12.如权利要求11所述的计算机可读存储介质,其特征在于,还包括在被执行时使所 述至少一个处理器执行以下动作的计算机可执行指令恢复所述被挂起协同例程,其中所恢复的协同例程返回到所述恢复器,并且所述恢复 器将所述调用者的栈帧复制到所述线性机器调用栈上。
13.如权利要求11所述的计算机可读存储介质,其特征在于,还包括在被执行时使所 述至少一个处理器执行以下动作的计算机可执行指令支持异步编程。
14.如权利要求11所述的计算机可读存储介质,其特征在于,还包括在被执行时使所 述至少一个处理器执行以下动作的计算机可执行指令响应于所述调用者请求附加信息,恢复所述被挂起协同例程。
15.如权利要求11所述的计算机可读存储介质,其特征在于,还包括在被执行时使所 述至少一个处理器执行以下动作的计算机可执行指令响应于返回所请求的信息,恢复所述协同例程。
全文摘要
未挂起协同例程由机器调用栈机制来处理,其中该栈随着作出递归调用和从递归调用返回而增长和收缩。然而,在协同例程挂起时,执行附加的调用栈处理。发出挂起消息,并且调用栈的整个能恢复部分被移除并被复制到堆。将控制返回给驱动程序方法(恢复器)的帧被复制到调用栈,从而协同例程的恢复不会递归地重新激活整个调用栈。相反,恢复器仅重新激活被称为叶帧的最顶部或最当前帧。在协同例程挂起时,其不返回到其调用者,而是返回到已重新激活它的恢复器。
文档编号G06F9/48GK102141937SQ20111000847
公开日2011年8月3日 申请日期2011年1月6日 优先权日2010年1月7日
发明者H·J·M·梅杰, M·托格森, N·M·加夫特, N·古斯塔夫松 申请人:微软公司