ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/State.xs
(Generate patch)

Comparing Coro/Coro/State.xs (file contents):
Revision 1.235 by root, Sat May 10 00:13:18 2008 UTC vs.
Revision 1.244 by root, Sun Sep 21 18:29:39 2008 UTC

361 361
362 /* casting is fun. */ 362 /* casting is fun. */
363 while (&PL_sv_undef != (SV *)(padlist = (AV *)av_pop (av))) 363 while (&PL_sv_undef != (SV *)(padlist = (AV *)av_pop (av)))
364 free_padlist (aTHX_ padlist); 364 free_padlist (aTHX_ padlist);
365 365
366 SvREFCNT_dec (av);
367
366 return 0; 368 return 0;
367} 369}
368 370
369#define CORO_MAGIC_type_cv PERL_MAGIC_ext 371#define CORO_MAGIC_type_cv PERL_MAGIC_ext
370#define CORO_MAGIC_type_state PERL_MAGIC_ext 372#define CORO_MAGIC_type_state PERL_MAGIC_ext
420 if (expect_true (mg && AvFILLp ((av = (AV *)mg->mg_obj)) >= 0)) 422 if (expect_true (mg && AvFILLp ((av = (AV *)mg->mg_obj)) >= 0))
421 CvPADLIST (cv) = (AV *)AvARRAY (av)[AvFILLp (av)--]; 423 CvPADLIST (cv) = (AV *)AvARRAY (av)[AvFILLp (av)--];
422 else 424 else
423 { 425 {
424#if CORO_PREFER_PERL_FUNCTIONS 426#if CORO_PREFER_PERL_FUNCTIONS
425 /* this is probably cleaner, but also slower? */ 427 /* this is probably cleaner? but also slower! */
428 /* in practise, it seems to be less stable */
426 CV *cp = Perl_cv_clone (cv); 429 CV *cp = Perl_cv_clone (cv);
427 CvPADLIST (cv) = CvPADLIST (cp); 430 CvPADLIST (cv) = CvPADLIST (cp);
428 CvPADLIST (cp) = 0; 431 CvPADLIST (cp) = 0;
429 SvREFCNT_dec (cp); 432 SvREFCNT_dec (cp);
430#else 433#else
683 686
684/** coroutine stack handling ************************************************/ 687/** coroutine stack handling ************************************************/
685 688
686static int (*orig_sigelem_get) (pTHX_ SV *sv, MAGIC *mg); 689static int (*orig_sigelem_get) (pTHX_ SV *sv, MAGIC *mg);
687static int (*orig_sigelem_set) (pTHX_ SV *sv, MAGIC *mg); 690static int (*orig_sigelem_set) (pTHX_ SV *sv, MAGIC *mg);
691static int (*orig_sigelem_clr) (pTHX_ SV *sv, MAGIC *mg);
692
693/* apparently < 5.8.8 */
694#ifndef MgPV_nolen_const
695#define MgPV_nolen_const(mg) (((((int)(mg)->mg_len)) == HEf_SVKEY) ? \
696 SvPV_nolen((SV*)((mg)->mg_ptr)) : \
697 (const char*)(mg)->mg_ptr)
698#endif
688 699
689/* 700/*
690 * This overrides the default magic get method of %SIG elements. 701 * This overrides the default magic get method of %SIG elements.
691 * The original one doesn't provide for reading back of PL_diehook/PL_warnhook 702 * The original one doesn't provide for reading back of PL_diehook/PL_warnhook
692 * and instead of tryign to save and restore the hash elements, we just provide 703 * and instead of tryign to save and restore the hash elements, we just provide
700{ 711{
701 const char *s = MgPV_nolen_const (mg); 712 const char *s = MgPV_nolen_const (mg);
702 713
703 if (*s == '_') 714 if (*s == '_')
704 { 715 {
705 if (strEQ (s, "__DIE__" ) && PL_diehook ) return sv_setsv (sv, PL_diehook ), 0; 716 SV **svp = 0;
706 if (strEQ (s, "__WARN__") && PL_warnhook) return sv_setsv (sv, PL_warnhook), 0; 717
718 if (strEQ (s, "__DIE__" )) svp = &PL_diehook;
719 if (strEQ (s, "__WARN__")) svp = &PL_warnhook;
720
721 if (svp)
722 {
723 sv_setsv (sv, *svp ? *svp : &PL_sv_undef);
724 return 0;
725 }
707 } 726 }
708 727
709 return orig_sigelem_get ? orig_sigelem_get (aTHX_ sv, mg) : 0; 728 return orig_sigelem_get ? orig_sigelem_get (aTHX_ sv, mg) : 0;
729}
730
731static int
732coro_sigelem_clr (pTHX_ SV *sv, MAGIC *mg)
733{
734 const char *s = MgPV_nolen_const (mg);
735
736 if (*s == '_')
737 {
738 SV **svp = 0;
739
740 if (strEQ (s, "__DIE__" )) svp = &PL_diehook;
741 if (strEQ (s, "__WARN__")) svp = &PL_warnhook;
742
743 if (svp)
744 {
745 SV *old = *svp;
746 *svp = 0;
747 SvREFCNT_dec (old);
748 return 0;
749 }
750 }
751
752 return orig_sigelem_clr ? orig_sigelem_clr (aTHX_ sv, mg) : 0;
710} 753}
711 754
712static int 755static int
713coro_sigelem_set (pTHX_ SV *sv, MAGIC *mg) 756coro_sigelem_set (pTHX_ SV *sv, MAGIC *mg)
714{ 757{
1383static int 1426static int
1384api_ready (SV *coro_sv) 1427api_ready (SV *coro_sv)
1385{ 1428{
1386 dTHX; 1429 dTHX;
1387 struct coro *coro; 1430 struct coro *coro;
1388 SV *hook; 1431 SV *sv_hook;
1432 void (*xs_hook)(void);
1389 1433
1390 if (SvROK (coro_sv)) 1434 if (SvROK (coro_sv))
1391 coro_sv = SvRV (coro_sv); 1435 coro_sv = SvRV (coro_sv);
1392 1436
1393 coro = SvSTATE (coro_sv); 1437 coro = SvSTATE (coro_sv);
1397 1441
1398 coro->flags |= CF_READY; 1442 coro->flags |= CF_READY;
1399 1443
1400 LOCK; 1444 LOCK;
1401 1445
1402 hook = coro_nready ? 0 : coro_readyhook; 1446 sv_hook = coro_nready ? 0 : coro_readyhook;
1447 xs_hook = coro_nready ? 0 : coroapi.readyhook;
1403 1448
1404 coro_enq (aTHX_ SvREFCNT_inc (coro_sv)); 1449 coro_enq (aTHX_ SvREFCNT_inc (coro_sv));
1405 ++coro_nready; 1450 ++coro_nready;
1406 1451
1407 UNLOCK; 1452 UNLOCK;
1408 1453
1409 if (hook) 1454 if (sv_hook)
1410 { 1455 {
1411 dSP; 1456 dSP;
1412 1457
1413 ENTER; 1458 ENTER;
1414 SAVETMPS; 1459 SAVETMPS;
1415 1460
1416 PUSHMARK (SP); 1461 PUSHMARK (SP);
1417 PUTBACK; 1462 PUTBACK;
1418 call_sv (hook, G_DISCARD); 1463 call_sv (sv_hook, G_DISCARD);
1419 SPAGAIN; 1464 SPAGAIN;
1420 1465
1421 FREETMPS; 1466 FREETMPS;
1422 LEAVE; 1467 LEAVE;
1423 } 1468 }
1469
1470 if (xs_hook)
1471 xs_hook ();
1424 1472
1425 return 1; 1473 return 1;
1426} 1474}
1427 1475
1428static int 1476static int
1578 else 1626 else
1579 coro->slot->runops = RUNOPS_DEFAULT; 1627 coro->slot->runops = RUNOPS_DEFAULT;
1580 } 1628 }
1581} 1629}
1582 1630
1631static int
1632coro_gensub_free (pTHX_ SV *sv, MAGIC *mg)
1633{
1634 AV *padlist;
1635 AV *av = (AV *)mg->mg_obj;
1636
1637 abort ();
1638
1639 return 0;
1640}
1641
1642static MGVTBL coro_gensub_vtbl = {
1643 0, 0, 0, 0,
1644 coro_gensub_free
1645};
1646
1583MODULE = Coro::State PACKAGE = Coro::State PREFIX = api_ 1647MODULE = Coro::State PACKAGE = Coro::State PREFIX = api_
1584 1648
1585PROTOTYPES: DISABLE 1649PROTOTYPES: DISABLE
1586 1650
1587BOOT: 1651BOOT:
1592 BOOT_PAGESIZE; 1656 BOOT_PAGESIZE;
1593 1657
1594 irsgv = gv_fetchpv ("/" , GV_ADD|GV_NOTQUAL, SVt_PV); 1658 irsgv = gv_fetchpv ("/" , GV_ADD|GV_NOTQUAL, SVt_PV);
1595 stdoutgv = gv_fetchpv ("STDOUT", GV_ADD|GV_NOTQUAL, SVt_PVIO); 1659 stdoutgv = gv_fetchpv ("STDOUT", GV_ADD|GV_NOTQUAL, SVt_PVIO);
1596 1660
1597 orig_sigelem_get = PL_vtbl_sigelem.svt_get; 1661 orig_sigelem_get = PL_vtbl_sigelem.svt_get; PL_vtbl_sigelem.svt_get = coro_sigelem_get;
1598 PL_vtbl_sigelem.svt_get = coro_sigelem_get; 1662 orig_sigelem_set = PL_vtbl_sigelem.svt_set; PL_vtbl_sigelem.svt_set = coro_sigelem_set;
1599 orig_sigelem_set = PL_vtbl_sigelem.svt_set; 1663 orig_sigelem_clr = PL_vtbl_sigelem.svt_clear; PL_vtbl_sigelem.svt_clear = coro_sigelem_clr;
1600 PL_vtbl_sigelem.svt_set = coro_sigelem_set;
1601 1664
1602 hv_sig = coro_get_hv (aTHX_ "SIG", TRUE); 1665 hv_sig = coro_get_hv (aTHX_ "SIG", TRUE);
1603 rv_diehook = newRV_inc ((SV *)gv_fetchpv ("Coro::State::diehook" , 0, SVt_PVCV)); 1666 rv_diehook = newRV_inc ((SV *)gv_fetchpv ("Coro::State::diehook" , 0, SVt_PVCV));
1604 rv_warnhook = newRV_inc ((SV *)gv_fetchpv ("Coro::State::warnhook", 0, SVt_PVCV)); 1667 rv_warnhook = newRV_inc ((SV *)gv_fetchpv ("Coro::State::warnhook", 0, SVt_PVCV));
1605 1668
1614 main_top_env = PL_top_env; 1677 main_top_env = PL_top_env;
1615 1678
1616 while (main_top_env->je_prev) 1679 while (main_top_env->je_prev)
1617 main_top_env = main_top_env->je_prev; 1680 main_top_env = main_top_env->je_prev;
1618 1681
1619 coroapi.ver = CORO_API_VERSION; 1682 coroapi.ver = CORO_API_VERSION;
1620 coroapi.rev = CORO_API_REVISION; 1683 coroapi.rev = CORO_API_REVISION;
1621 coroapi.transfer = api_transfer; 1684 coroapi.transfer = api_transfer;
1622 1685
1623 assert (("PRIO_NORMAL must be 0", !PRIO_NORMAL)); 1686 assert (("PRIO_NORMAL must be 0", !PRIO_NORMAL));
1624} 1687}
1625 1688
1626SV * 1689SV *
1846force_cctx () 1909force_cctx ()
1847 CODE: 1910 CODE:
1848 struct coro *coro = SvSTATE (coro_current); 1911 struct coro *coro = SvSTATE (coro_current);
1849 coro->cctx->idle_sp = 0; 1912 coro->cctx->idle_sp = 0;
1850 1913
1851MODULE = Coro::State PACKAGE = Coro
1852
1853BOOT:
1854{
1855 int i;
1856
1857 av_async_pool = coro_get_av (aTHX_ "Coro::async_pool", TRUE);
1858 sv_pool_rss = coro_get_sv (aTHX_ "Coro::POOL_RSS" , TRUE);
1859 sv_pool_size = coro_get_sv (aTHX_ "Coro::POOL_SIZE" , TRUE);
1860
1861 coro_current = coro_get_sv (aTHX_ "Coro::current", FALSE);
1862 SvREADONLY_on (coro_current);
1863
1864 coro_stash = gv_stashpv ("Coro", TRUE);
1865
1866 newCONSTSUB (coro_stash, "PRIO_MAX", newSViv (PRIO_MAX));
1867 newCONSTSUB (coro_stash, "PRIO_HIGH", newSViv (PRIO_HIGH));
1868 newCONSTSUB (coro_stash, "PRIO_NORMAL", newSViv (PRIO_NORMAL));
1869 newCONSTSUB (coro_stash, "PRIO_LOW", newSViv (PRIO_LOW));
1870 newCONSTSUB (coro_stash, "PRIO_IDLE", newSViv (PRIO_IDLE));
1871 newCONSTSUB (coro_stash, "PRIO_MIN", newSViv (PRIO_MIN));
1872
1873 for (i = PRIO_MAX - PRIO_MIN + 1; i--; )
1874 coro_ready[i] = newAV ();
1875
1876 {
1877 SV *sv = perl_get_sv ("Coro::API", TRUE);
1878 perl_get_sv ("Coro::API", TRUE); /* silence 5.10 warning */
1879
1880 coroapi.schedule = api_schedule;
1881 coroapi.cede = api_cede;
1882 coroapi.cede_notself = api_cede_notself;
1883 coroapi.ready = api_ready;
1884 coroapi.is_ready = api_is_ready;
1885 coroapi.nready = &coro_nready;
1886 coroapi.current = coro_current;
1887
1888 GCoroAPI = &coroapi;
1889 sv_setiv (sv, (IV)&coroapi);
1890 SvREADONLY_on (sv);
1891 }
1892}
1893
1894void
1895_set_current (SV *current)
1896 PROTOTYPE: $
1897 CODE:
1898 SvREFCNT_dec (SvRV (coro_current));
1899 SvRV_set (coro_current, SvREFCNT_inc (SvRV (current)));
1900
1901void
1902_set_readyhook (SV *hook)
1903 PROTOTYPE: $
1904 CODE:
1905 LOCK;
1906 if (coro_readyhook)
1907 SvREFCNT_dec (coro_readyhook);
1908 coro_readyhook = SvOK (hook) ? newSVsv (hook) : 0;
1909 UNLOCK;
1910
1911int
1912prio (Coro::State coro, int newprio = 0)
1913 ALIAS:
1914 nice = 1
1915 CODE:
1916{
1917 RETVAL = coro->prio;
1918
1919 if (items > 1)
1920 {
1921 if (ix)
1922 newprio = coro->prio - newprio;
1923
1924 if (newprio < PRIO_MIN) newprio = PRIO_MIN;
1925 if (newprio > PRIO_MAX) newprio = PRIO_MAX;
1926
1927 coro->prio = newprio;
1928 }
1929}
1930 OUTPUT:
1931 RETVAL
1932
1933SV *
1934ready (SV *self)
1935 PROTOTYPE: $
1936 CODE:
1937 RETVAL = boolSV (api_ready (self));
1938 OUTPUT:
1939 RETVAL
1940
1941int
1942nready (...)
1943 PROTOTYPE:
1944 CODE:
1945 RETVAL = coro_nready;
1946 OUTPUT:
1947 RETVAL
1948
1949void 1914void
1950throw (Coro::State self, SV *throw = &PL_sv_undef) 1915throw (Coro::State self, SV *throw = &PL_sv_undef)
1951 PROTOTYPE: $;$ 1916 PROTOTYPE: $;$
1952 CODE: 1917 CODE:
1953 SvREFCNT_dec (self->throw); 1918 SvREFCNT_dec (self->throw);
1967 SV **dst = ix ? (SV **)&self->slot->defav : (SV **)&self->slot->defsv; 1932 SV **dst = ix ? (SV **)&self->slot->defav : (SV **)&self->slot->defsv;
1968 1933
1969 SV *tmp = *src; *src = *dst; *dst = tmp; 1934 SV *tmp = *src; *src = *dst; *dst = tmp;
1970 } 1935 }
1971 1936
1937MODULE = Coro::State PACKAGE = Coro
1938
1939BOOT:
1940{
1941 int i;
1942
1943 av_async_pool = coro_get_av (aTHX_ "Coro::async_pool", TRUE);
1944 sv_pool_rss = coro_get_sv (aTHX_ "Coro::POOL_RSS" , TRUE);
1945 sv_pool_size = coro_get_sv (aTHX_ "Coro::POOL_SIZE" , TRUE);
1946
1947 coro_current = coro_get_sv (aTHX_ "Coro::current", FALSE);
1948 SvREADONLY_on (coro_current);
1949
1950 coro_stash = gv_stashpv ("Coro", TRUE);
1951
1952 newCONSTSUB (coro_stash, "PRIO_MAX", newSViv (PRIO_MAX));
1953 newCONSTSUB (coro_stash, "PRIO_HIGH", newSViv (PRIO_HIGH));
1954 newCONSTSUB (coro_stash, "PRIO_NORMAL", newSViv (PRIO_NORMAL));
1955 newCONSTSUB (coro_stash, "PRIO_LOW", newSViv (PRIO_LOW));
1956 newCONSTSUB (coro_stash, "PRIO_IDLE", newSViv (PRIO_IDLE));
1957 newCONSTSUB (coro_stash, "PRIO_MIN", newSViv (PRIO_MIN));
1958
1959 for (i = PRIO_MAX - PRIO_MIN + 1; i--; )
1960 coro_ready[i] = newAV ();
1961
1962 {
1963 SV *sv = perl_get_sv ("Coro::API", TRUE);
1964 perl_get_sv ("Coro::API", TRUE); /* silence 5.10 warning */
1965
1966 coroapi.schedule = api_schedule;
1967 coroapi.cede = api_cede;
1968 coroapi.cede_notself = api_cede_notself;
1969 coroapi.ready = api_ready;
1970 coroapi.is_ready = api_is_ready;
1971 coroapi.nready = &coro_nready;
1972 coroapi.current = coro_current;
1973
1974 GCoroAPI = &coroapi;
1975 sv_setiv (sv, (IV)&coroapi);
1976 SvREADONLY_on (sv);
1977 }
1978}
1979
1980void
1981_set_current (SV *current)
1982 PROTOTYPE: $
1983 CODE:
1984 SvREFCNT_dec (SvRV (coro_current));
1985 SvRV_set (coro_current, SvREFCNT_inc (SvRV (current)));
1986
1987void
1988_set_readyhook (SV *hook)
1989 PROTOTYPE: $
1990 CODE:
1991 LOCK;
1992 SvREFCNT_dec (coro_readyhook);
1993 coro_readyhook = SvOK (hook) ? newSVsv (hook) : 0;
1994 UNLOCK;
1995
1996int
1997prio (Coro::State coro, int newprio = 0)
1998 ALIAS:
1999 nice = 1
2000 CODE:
2001{
2002 RETVAL = coro->prio;
2003
2004 if (items > 1)
2005 {
2006 if (ix)
2007 newprio = coro->prio - newprio;
2008
2009 if (newprio < PRIO_MIN) newprio = PRIO_MIN;
2010 if (newprio > PRIO_MAX) newprio = PRIO_MAX;
2011
2012 coro->prio = newprio;
2013 }
2014}
2015 OUTPUT:
2016 RETVAL
2017
2018SV *
2019ready (SV *self)
2020 PROTOTYPE: $
2021 CODE:
2022 RETVAL = boolSV (api_ready (self));
2023 OUTPUT:
2024 RETVAL
2025
2026int
2027nready (...)
2028 PROTOTYPE:
2029 CODE:
2030 RETVAL = coro_nready;
2031 OUTPUT:
2032 RETVAL
2033
1972# for async_pool speedup 2034# for async_pool speedup
1973void 2035void
1974_pool_1 (SV *cb) 2036_pool_1 (SV *cb)
1975 CODE: 2037 CODE:
1976{ 2038{
1981 AV *invoke_av; 2043 AV *invoke_av;
1982 int i, len; 2044 int i, len;
1983 2045
1984 if (!invoke) 2046 if (!invoke)
1985 { 2047 {
1986 SvREFCNT_dec (PL_diehook); PL_diehook = 0; 2048 SV *old = PL_diehook;
2049 PL_diehook = 0;
2050 SvREFCNT_dec (old);
1987 croak ("\3async_pool terminate\2\n"); 2051 croak ("\3async_pool terminate\2\n");
1988 } 2052 }
1989 2053
1990 SvREFCNT_dec (coro->saved_deffh); 2054 SvREFCNT_dec (coro->saved_deffh);
1991 coro->saved_deffh = SvREFCNT_inc ((SV *)PL_defoutgv); 2055 coro->saved_deffh = SvREFCNT_inc ((SV *)PL_defoutgv);
2020 coro->saved_deffh = 0; 2084 coro->saved_deffh = 0;
2021 2085
2022 if (coro_rss (aTHX_ coro) > SvIV (sv_pool_rss) 2086 if (coro_rss (aTHX_ coro) > SvIV (sv_pool_rss)
2023 || av_len (av_async_pool) + 1 >= SvIV (sv_pool_size)) 2087 || av_len (av_async_pool) + 1 >= SvIV (sv_pool_size))
2024 { 2088 {
2025 SvREFCNT_dec (PL_diehook); PL_diehook = 0; 2089 SV *old = PL_diehook;
2090 PL_diehook = 0;
2091 SvREFCNT_dec (old);
2026 croak ("\3async_pool terminate\2\n"); 2092 croak ("\3async_pool terminate\2\n");
2027 } 2093 }
2028 2094
2029 av_clear (GvAV (PL_defgv)); 2095 av_clear (GvAV (PL_defgv));
2030 hv_store ((HV *)SvRV (coro_current), "desc", sizeof ("desc") - 1, 2096 hv_store ((HV *)SvRV (coro_current), "desc", sizeof ("desc") - 1,
2035 if (coro->cctx && (coro->cctx->flags & CC_TRACE)) 2101 if (coro->cctx && (coro->cctx->flags & CC_TRACE))
2036 api_trace (coro_current, 0); 2102 api_trace (coro_current, 0);
2037 2103
2038 av_push (av_async_pool, newSVsv (coro_current)); 2104 av_push (av_async_pool, newSVsv (coro_current));
2039} 2105}
2106
2107#if 0
2108
2109void
2110_generator_call (...)
2111 PROTOTYPE: @
2112 PPCODE:
2113 fprintf (stderr, "call %p\n", CvXSUBANY(cv).any_ptr);
2114 xxxx
2115 abort ();
2116
2117SV *
2118gensub (SV *sub, ...)
2119 PROTOTYPE: &;@
2120 CODE:
2121{
2122 struct coro *coro;
2123 MAGIC *mg;
2124 CV *xcv;
2125 CV *ncv = (CV *)newSV_type (SVt_PVCV);
2126 int i;
2127
2128 CvGV (ncv) = CvGV (cv);
2129 CvFILE (ncv) = CvFILE (cv);
2130
2131 Newz (0, coro, 1, struct coro);
2132 coro->args = newAV ();
2133 coro->flags = CF_NEW;
2134
2135 av_extend (coro->args, items - 1);
2136 for (i = 1; i < items; i++)
2137 av_push (coro->args, newSVsv (ST (i)));
2138
2139 CvISXSUB_on (ncv);
2140 CvXSUBANY (ncv).any_ptr = (void *)coro;
2141
2142 xcv = GvCV (gv_fetchpv ("Coro::_generator_call", 0, SVt_PVCV));
2143
2144 CvXSUB (ncv) = CvXSUB (xcv);
2145 CvANON_on (ncv);
2146
2147 mg = sv_magicext ((SV *)ncv, 0, CORO_MAGIC_type_state, &coro_gensub_vtbl, (char *)coro, 0);
2148 RETVAL = newRV_noinc ((SV *)ncv);
2149}
2150 OUTPUT:
2151 RETVAL
2152
2153#endif
2040 2154
2041 2155
2042MODULE = Coro::State PACKAGE = Coro::AIO 2156MODULE = Coro::State PACKAGE = Coro::AIO
2043 2157
2044SV * 2158SV *
2078 2192
2079BOOT: 2193BOOT:
2080 sv_activity = coro_get_sv (aTHX_ "Coro::AnyEvent::ACTIVITY", TRUE); 2194 sv_activity = coro_get_sv (aTHX_ "Coro::AnyEvent::ACTIVITY", TRUE);
2081 2195
2082SV * 2196SV *
2083_schedule () 2197_schedule (...)
2084 PROTOTYPE: @ 2198 PROTOTYPE: @
2085 CODE: 2199 CODE:
2086{ 2200{
2087 static int incede; 2201 static int incede;
2088 2202

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines